Compare commits

...

11 Commits

Author SHA1 Message Date
lukasadrion
133827c2bd add interactive map with leaflet 2026-01-21 12:54:03 +01:00
lukasadrion
93f4e3e81d 📝 add more documentation 2026-01-21 00:21:50 +01:00
lukasadrion
86a9e4163f 🐛 fixed the credentials bug for documentation 2026-01-20 23:36:07 +01:00
lukasadrion
387e1caa7f 📝 add documentation to main 2026-01-20 23:10:08 +01:00
lukasadrion
696f52eda3 ♻️ refactor all logic to main.rmd 2026-01-20 16:53:19 +01:00
lukasadrion
eb49746268 🩹 fix small issues in main 2026-01-20 16:13:41 +01:00
eneller
f491345ea0 refactor: consolidate main.Rmd statistics 2026-01-20 15:46:26 +01:00
lukasadrion
aacdc12638 ♻️ refactor app.Rmd 2026-01-20 15:31:21 +01:00
eneller
e4c7ce4977 chore: move app from R to Rmd 2026-01-20 13:48:49 +01:00
eneller
d9a33a5d2b build: add dependencies 2026-01-20 12:05:35 +01:00
Patrik M
d8dd920d6b Added trajectories and alternative GUI 2026-01-19 17:25:18 +01:00
3 changed files with 1242 additions and 56 deletions

312
renv.lock
View File

@@ -623,6 +623,30 @@
"Author": "Martin Maechler [aut, cre] (<https://orcid.org/0000-0002-8685-9910>), Peter Rousseeuw [aut] (Fortran original, <https://orcid.org/0000-0002-3807-5353>), Anja Struyf [aut] (S original), Mia Hubert [aut] (S original, <https://orcid.org/0000-0001-6398-4850>), Kurt Hornik [trl, ctb] (port to R; maintenance(1999-2000), <https://orcid.org/0000-0003-4198-9911>), Matthias Studer [ctb], Pierre Roudier [ctb], Juan Gonzalez [ctb], Kamil Kozlowski [ctb], Erich Schubert [ctb] (fastpam options for pam(), <https://orcid.org/0000-0001-9143-4880>), Keefe Murphy [ctb] (volume.ellipsoid({d >= 3}))", "Author": "Martin Maechler [aut, cre] (<https://orcid.org/0000-0002-8685-9910>), Peter Rousseeuw [aut] (Fortran original, <https://orcid.org/0000-0002-3807-5353>), Anja Struyf [aut] (S original), Mia Hubert [aut] (S original, <https://orcid.org/0000-0001-6398-4850>), Kurt Hornik [trl, ctb] (port to R; maintenance(1999-2000), <https://orcid.org/0000-0003-4198-9911>), Matthias Studer [ctb], Pierre Roudier [ctb], Juan Gonzalez [ctb], Kamil Kozlowski [ctb], Erich Schubert [ctb] (fastpam options for pam(), <https://orcid.org/0000-0001-9143-4880>), Keefe Murphy [ctb] (volume.ellipsoid({d >= 3}))",
"Repository": "CRAN" "Repository": "CRAN"
}, },
"commonmark": {
"Package": "commonmark",
"Version": "2.0.0",
"Source": "Repository",
"Type": "Package",
"Title": "High Performance CommonMark and Github Markdown Rendering in R",
"Authors@R": "c( person(\"Jeroen\", \"Ooms\", ,\"jeroenooms@gmail.com\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4035-0289\")), person(\"John MacFarlane\", role = \"cph\", comment = \"Author of cmark\"))",
"Description": "The CommonMark specification <https://github.github.com/gfm/> defines a rationalized version of markdown syntax. This package uses the 'cmark' reference implementation for converting markdown text into various formats including html, latex and groff man. In addition it exposes the markdown parse tree in xml format. Also includes opt-in support for GFM extensions including tables, autolinks, and strikethrough text.",
"License": "BSD_2_clause + file LICENSE",
"URL": "https://docs.ropensci.org/commonmark/ https://ropensci.r-universe.dev/commonmark",
"BugReports": "https://github.com/r-lib/commonmark/issues",
"Suggests": [
"curl",
"testthat",
"xml2"
],
"RoxygenNote": "7.3.2",
"Language": "en-US",
"Encoding": "UTF-8",
"NeedsCompilation": "yes",
"Author": "Jeroen Ooms [aut, cre] (ORCID: <https://orcid.org/0000-0002-4035-0289>), John MacFarlane [cph] (Author of cmark)",
"Maintainer": "Jeroen Ooms <jeroenooms@gmail.com>",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"cpp11": { "cpp11": {
"Package": "cpp11", "Package": "cpp11",
"Version": "0.5.2", "Version": "0.5.2",
@@ -1475,6 +1499,47 @@
"Maintainer": "Carson Sievert <carson@posit.co>", "Maintainer": "Carson Sievert <carson@posit.co>",
"Repository": "https://packagemanager.posit.co/cran/latest" "Repository": "https://packagemanager.posit.co/cran/latest"
}, },
"httpuv": {
"Package": "httpuv",
"Version": "1.6.16",
"Source": "Repository",
"Type": "Package",
"Title": "HTTP and WebSocket Server Library",
"Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = c(\"aut\", \"cre\")), person(\"Posit, PBC\", \"fnd\", role = \"cph\"), person(\"Hector\", \"Corrada Bravo\", role = \"ctb\"), person(\"Jeroen\", \"Ooms\", role = \"ctb\"), person(\"Andrzej\", \"Krzemienski\", role = \"cph\", comment = \"optional.hpp\"), person(\"libuv project contributors\", role = \"cph\", comment = \"libuv library, see src/libuv/AUTHORS file\"), person(\"Joyent, Inc. and other Node contributors\", role = \"cph\", comment = \"libuv library, see src/libuv/AUTHORS file; and http-parser library, see src/http-parser/AUTHORS file\"), person(\"Niels\", \"Provos\", role = \"cph\", comment = \"libuv subcomponent: tree.h\"), person(\"Internet Systems Consortium, Inc.\", role = \"cph\", comment = \"libuv subcomponent: inet_pton and inet_ntop, contained in src/libuv/src/inet.c\"), person(\"Alexander\", \"Chemeris\", role = \"cph\", comment = \"libuv subcomponent: stdint-msvc2008.h (from msinttypes)\"), person(\"Google, Inc.\", role = \"cph\", comment = \"libuv subcomponent: pthread-fixes.c\"), person(\"Sony Mobile Communcations AB\", role = \"cph\", comment = \"libuv subcomponent: pthread-fixes.c\"), person(\"Berkeley Software Design Inc.\", role = \"cph\", comment = \"libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c\"), person(\"Kenneth\", \"MacKay\", role = \"cph\", comment = \"libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c\"), person(\"Emergya (Cloud4all, FP7/2007-2013, grant agreement no 289016)\", role = \"cph\", comment = \"libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c\"), person(\"Steve\", \"Reid\", role = \"aut\", comment = \"SHA-1 implementation\"), person(\"James\", \"Brown\", role = \"aut\", comment = \"SHA-1 implementation\"), person(\"Bob\", \"Trower\", role = \"aut\", comment = \"base64 implementation\"), person(\"Alexander\", \"Peslyak\", role = \"aut\", comment = \"MD5 implementation\"), person(\"Trantor Standard Systems\", role = \"cph\", comment = \"base64 implementation\"), person(\"Igor\", \"Sysoev\", role = \"cph\", comment = \"http-parser\") )",
"Description": "Provides low-level socket and protocol support for handling HTTP and WebSocket requests directly from within R. It is primarily intended as a building block for other packages, rather than making it particularly easy to create complete web applications using httpuv alone. httpuv is built on top of the libuv and http-parser C libraries, both of which were developed by Joyent, Inc. (See LICENSE file for libuv and http-parser license information.)",
"License": "GPL (>= 2) | file LICENSE",
"URL": "https://github.com/rstudio/httpuv",
"BugReports": "https://github.com/rstudio/httpuv/issues",
"Depends": [
"R (>= 2.15.1)"
],
"Imports": [
"later (>= 0.8.0)",
"promises",
"R6",
"Rcpp (>= 1.0.7)",
"utils"
],
"Suggests": [
"callr",
"curl",
"jsonlite",
"testthat",
"websocket"
],
"LinkingTo": [
"later",
"Rcpp"
],
"Encoding": "UTF-8",
"RoxygenNote": "7.3.2",
"SystemRequirements": "GNU make, zlib",
"Collate": "'RcppExports.R' 'httpuv.R' 'random_port.R' 'server.R' 'staticServer.R' 'static_paths.R' 'utils.R'",
"NeedsCompilation": "yes",
"Author": "Joe Cheng [aut], Winston Chang [aut, cre], Posit, PBC fnd [cph], Hector Corrada Bravo [ctb], Jeroen Ooms [ctb], Andrzej Krzemienski [cph] (optional.hpp), libuv project contributors [cph] (libuv library, see src/libuv/AUTHORS file), Joyent, Inc. and other Node contributors [cph] (libuv library, see src/libuv/AUTHORS file; and http-parser library, see src/http-parser/AUTHORS file), Niels Provos [cph] (libuv subcomponent: tree.h), Internet Systems Consortium, Inc. [cph] (libuv subcomponent: inet_pton and inet_ntop, contained in src/libuv/src/inet.c), Alexander Chemeris [cph] (libuv subcomponent: stdint-msvc2008.h (from msinttypes)), Google, Inc. [cph] (libuv subcomponent: pthread-fixes.c), Sony Mobile Communcations AB [cph] (libuv subcomponent: pthread-fixes.c), Berkeley Software Design Inc. [cph] (libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c), Kenneth MacKay [cph] (libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c), Emergya (Cloud4all, FP7/2007-2013, grant agreement no 289016) [cph] (libuv subcomponent: android-ifaddrs.h, android-ifaddrs.c), Steve Reid [aut] (SHA-1 implementation), James Brown [aut] (SHA-1 implementation), Bob Trower [aut] (base64 implementation), Alexander Peslyak [aut] (MD5 implementation), Trantor Standard Systems [cph] (base64 implementation), Igor Sysoev [cph] (http-parser)",
"Maintainer": "Winston Chang <winston@posit.co>",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"httr": { "httr": {
"Package": "httr", "Package": "httr",
"Version": "1.4.7", "Version": "1.4.7",
@@ -1714,6 +1779,46 @@
"Repository": "https://packagemanager.posit.co/cran/latest", "Repository": "https://packagemanager.posit.co/cran/latest",
"Encoding": "UTF-8" "Encoding": "UTF-8"
}, },
"later": {
"Package": "later",
"Version": "1.4.5",
"Source": "Repository",
"Type": "Package",
"Title": "Utilities for Scheduling Functions to Execute Later with Event Loops",
"Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Charlie\", \"Gao\", , \"charlie.gao@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-0750-061X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")), person(\"Marcus\", \"Geelnard\", role = c(\"ctb\", \"cph\"), comment = \"TinyCThread library, https://tinycthread.github.io/\"), person(\"Evan\", \"Nemerson\", role = c(\"ctb\", \"cph\"), comment = \"TinyCThread library, https://tinycthread.github.io/\") )",
"Description": "Executes arbitrary R or C functions some time after the current time, after the R execution stack has emptied. The functions are scheduled in an event loop.",
"License": "MIT + file LICENSE",
"URL": "https://later.r-lib.org, https://github.com/r-lib/later",
"BugReports": "https://github.com/r-lib/later/issues",
"Depends": [
"R (>= 3.5)"
],
"Imports": [
"Rcpp (>= 1.0.10)",
"rlang"
],
"Suggests": [
"knitr",
"nanonext",
"promises",
"rmarkdown",
"testthat (>= 3.0.0)"
],
"LinkingTo": [
"Rcpp"
],
"VignetteBuilder": "knitr",
"Config/build/compilation-database": "true",
"Config/Needs/website": "tidyverse/tidytemplate",
"Config/testthat/edition": "3",
"Config/usethis/last-upkeep": "2025-07-18",
"Encoding": "UTF-8",
"RoxygenNote": "7.3.3",
"NeedsCompilation": "yes",
"Author": "Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>), Joe Cheng [aut], Charlie Gao [aut, cre] (ORCID: <https://orcid.org/0000-0002-0750-061X>), Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>), Marcus Geelnard [ctb, cph] (TinyCThread library, https://tinycthread.github.io/), Evan Nemerson [ctb, cph] (TinyCThread library, https://tinycthread.github.io/)",
"Maintainer": "Charlie Gao <charlie.gao@posit.co>",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"lifecycle": { "lifecycle": {
"Package": "lifecycle", "Package": "lifecycle",
"Version": "1.0.4", "Version": "1.0.4",
@@ -2014,6 +2119,42 @@
"Maintainer": "Jeroen Ooms <jeroenooms@gmail.com>", "Maintainer": "Jeroen Ooms <jeroenooms@gmail.com>",
"Repository": "https://packagemanager.posit.co/cran/latest" "Repository": "https://packagemanager.posit.co/cran/latest"
}, },
"otel": {
"Package": "otel",
"Version": "0.2.0",
"Source": "Repository",
"Title": "OpenTelemetry R API",
"Authors@R": "person(\"Gábor\", \"Csárdi\", , \"csardi.gabor@gmail.com\", role = c(\"aut\", \"cre\"))",
"Description": "High-quality, ubiquitous, and portable telemetry to enable effective observability. OpenTelemetry is a collection of tools, APIs, and SDKs used to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior. This package implements the OpenTelemetry API: <https://opentelemetry.io/docs/specs/otel/>. Use this package as a dependency if you want to instrument your R package for OpenTelemetry.",
"License": "MIT + file LICENSE",
"Encoding": "UTF-8",
"RoxygenNote": "7.3.2.9000",
"Depends": [
"R (>= 3.6.0)"
],
"Suggests": [
"callr",
"cli",
"glue",
"jsonlite",
"otelsdk",
"processx",
"shiny",
"spelling",
"testthat (>= 3.0.0)",
"utils",
"withr"
],
"Config/Needs/website": "tidyverse/tidytemplate",
"Config/testthat/edition": "3",
"URL": "https://otel.r-lib.org, https://github.com/r-lib/otel",
"Additional_repositories": "https://github.com/r-lib/otelsdk/releases/download/devel",
"BugReports": "https://github.com/r-lib/otel/issues",
"NeedsCompilation": "no",
"Author": "Gábor Csárdi [aut, cre]",
"Maintainer": "Gábor Csárdi <csardi.gabor@gmail.com>",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"pillar": { "pillar": {
"Package": "pillar", "Package": "pillar",
"Version": "1.11.1", "Version": "1.11.1",
@@ -2232,6 +2373,53 @@
"Maintainer": "Gábor Csárdi <csardi.gabor@gmail.com>", "Maintainer": "Gábor Csárdi <csardi.gabor@gmail.com>",
"Repository": "https://packagemanager.posit.co/cran/latest" "Repository": "https://packagemanager.posit.co/cran/latest"
}, },
"promises": {
"Package": "promises",
"Version": "1.5.0",
"Source": "Repository",
"Type": "Package",
"Title": "Abstractions for Promise-Based Asynchronous Programming",
"Authors@R": "c( person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"Barret\", \"Schloerke\", , \"barret@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Charlie\", \"Gao\", , \"charlie.gao@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-0750-061X\")), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")) )",
"Description": "Provides fundamental abstractions for doing asynchronous programming in R using promises. Asynchronous programming is useful for allowing a single R process to orchestrate multiple tasks in the background while also attending to something else. Semantics are similar to 'JavaScript' promises, but with a syntax that is idiomatic R.",
"License": "MIT + file LICENSE",
"URL": "https://rstudio.github.io/promises/, https://github.com/rstudio/promises",
"BugReports": "https://github.com/rstudio/promises/issues",
"Depends": [
"R (>= 4.1.0)"
],
"Imports": [
"fastmap (>= 1.1.0)",
"later",
"lifecycle",
"magrittr (>= 1.5)",
"otel (>= 0.2.0)",
"R6",
"rlang"
],
"Suggests": [
"future (>= 1.21.0)",
"knitr",
"mirai",
"otelsdk (>= 0.2.0)",
"purrr",
"Rcpp",
"rmarkdown",
"spelling",
"testthat (>= 3.0.0)",
"vembedr"
],
"VignetteBuilder": "knitr",
"Config/Needs/website": "rsconnect, tidyverse/tidytemplate",
"Config/testthat/edition": "3",
"Config/usethis/last-upkeep": "2025-05-27",
"Encoding": "UTF-8",
"Language": "en-US",
"RoxygenNote": "7.3.3",
"NeedsCompilation": "no",
"Author": "Joe Cheng [aut], Barret Schloerke [aut, cre] (ORCID: <https://orcid.org/0000-0001-9986-114X>), Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>), Charlie Gao [aut] (ORCID: <https://orcid.org/0000-0002-0750-061X>), Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>)",
"Maintainer": "Barret Schloerke <barret@posit.co>",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"purrr": { "purrr": {
"Package": "purrr", "Package": "purrr",
"Version": "1.2.0", "Version": "1.2.0",
@@ -2607,6 +2795,78 @@
"Maintainer": "Thomas Lin Pedersen <thomas.pedersen@posit.co>", "Maintainer": "Thomas Lin Pedersen <thomas.pedersen@posit.co>",
"Repository": "https://packagemanager.posit.co/cran/latest" "Repository": "https://packagemanager.posit.co/cran/latest"
}, },
"shiny": {
"Package": "shiny",
"Version": "1.12.1",
"Source": "Repository",
"Type": "Package",
"Title": "Web Application Framework for R",
"Authors@R": "c( person(\"Winston\", \"Chang\", , \"winston@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0002-1576-2126\")), person(\"Joe\", \"Cheng\", , \"joe@posit.co\", role = \"aut\"), person(\"JJ\", \"Allaire\", , \"jj@posit.co\", role = \"aut\"), person(\"Carson\", \"Sievert\", , \"carson@posit.co\", role = c(\"aut\", \"cre\"), comment = c(ORCID = \"0000-0002-4958-2844\")), person(\"Barret\", \"Schloerke\", , \"barret@posit.co\", role = \"aut\", comment = c(ORCID = \"0000-0001-9986-114X\")), person(\"Garrick\", \"Aden-Buie\", , \"garrick@adenbuie.com\", role = \"aut\", comment = c(ORCID = \"0000-0002-7111-0077\")), person(\"Yihui\", \"Xie\", , \"yihui@posit.co\", role = \"aut\"), person(\"Jeff\", \"Allen\", role = \"aut\"), person(\"Jonathan\", \"McPherson\", , \"jonathan@posit.co\", role = \"aut\"), person(\"Alan\", \"Dipert\", role = \"aut\"), person(\"Barbara\", \"Borges\", role = \"aut\"), person(\"Posit Software, PBC\", role = c(\"cph\", \"fnd\"), comment = c(ROR = \"03wc8by49\")), person(, \"jQuery Foundation\", role = \"cph\", comment = \"jQuery library and jQuery UI library\"), person(, \"jQuery contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt\"), person(, \"jQuery UI contributors\", role = c(\"ctb\", \"cph\"), comment = \"jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt\"), person(\"Mark\", \"Otto\", role = \"ctb\", comment = \"Bootstrap library\"), person(\"Jacob\", \"Thornton\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Bootstrap contributors\", role = \"ctb\", comment = \"Bootstrap library\"), person(, \"Twitter, Inc\", role = \"cph\", comment = \"Bootstrap library\"), person(\"Prem Nawaz\", \"Khan\", role = \"ctb\", comment = \"Bootstrap accessibility plugin\"), person(\"Victor\", \"Tsaran\", role = \"ctb\", comment = \"Bootstrap accessibility plugin\"), person(\"Dennis\", \"Lembree\", role = \"ctb\", comment = \"Bootstrap accessibility plugin\"), person(\"Srinivasu\", \"Chakravarthula\", role = \"ctb\", comment = \"Bootstrap accessibility plugin\"), person(\"Cathy\", \"O'Connor\", role = \"ctb\", comment = \"Bootstrap accessibility plugin\"), person(, \"PayPal, Inc\", role = \"cph\", comment = \"Bootstrap accessibility plugin\"), person(\"Stefan\", \"Petre\", role = c(\"ctb\", \"cph\"), comment = \"Bootstrap-datepicker library\"), person(\"Andrew\", \"Rowls\", role = c(\"ctb\", \"cph\"), comment = \"Bootstrap-datepicker library\"), person(\"Brian\", \"Reavis\", role = c(\"ctb\", \"cph\"), comment = \"selectize.js library\"), person(\"Salmen\", \"Bejaoui\", role = c(\"ctb\", \"cph\"), comment = \"selectize-plugin-a11y library\"), person(\"Denis\", \"Ineshin\", role = c(\"ctb\", \"cph\"), comment = \"ion.rangeSlider library\"), person(\"Sami\", \"Samhuri\", role = c(\"ctb\", \"cph\"), comment = \"Javascript strftime library\"), person(, \"SpryMedia Limited\", role = c(\"ctb\", \"cph\"), comment = \"DataTables library\"), person(\"Ivan\", \"Sagalaev\", role = c(\"ctb\", \"cph\"), comment = \"highlight.js library\"), person(\"R Core Team\", role = c(\"ctb\", \"cph\"), comment = \"tar implementation from R\") )",
"Description": "Makes it incredibly easy to build interactive web applications with R. Automatic \"reactive\" binding between inputs and outputs and extensive prebuilt widgets make it possible to build beautiful, responsive, and powerful applications with minimal effort.",
"License": "GPL-3 | file LICENSE",
"URL": "https://shiny.posit.co/, https://github.com/rstudio/shiny",
"BugReports": "https://github.com/rstudio/shiny/issues",
"Depends": [
"methods",
"R (>= 3.0.2)"
],
"Imports": [
"bslib (>= 0.6.0)",
"cachem (>= 1.1.0)",
"cli",
"commonmark (>= 2.0.0)",
"fastmap (>= 1.1.1)",
"fontawesome (>= 0.4.0)",
"glue (>= 1.3.2)",
"grDevices",
"htmltools (>= 0.5.4)",
"httpuv (>= 1.5.2)",
"jsonlite (>= 0.9.16)",
"later (>= 1.0.0)",
"lifecycle (>= 0.2.0)",
"mime (>= 0.3)",
"otel",
"promises (>= 1.5.0)",
"R6 (>= 2.0)",
"rlang (>= 0.4.10)",
"sourcetools",
"tools",
"utils",
"withr",
"xtable"
],
"Suggests": [
"Cairo (>= 1.5-5)",
"coro (>= 1.1.0)",
"datasets",
"DT",
"dygraphs",
"future",
"ggplot2",
"knitr (>= 1.6)",
"magrittr",
"markdown",
"mirai",
"otelsdk (>= 0.2.0)",
"ragg",
"reactlog (>= 1.0.0)",
"rmarkdown",
"sass",
"showtext",
"testthat (>= 3.2.1)",
"watcher",
"yaml"
],
"Config/Needs/check": "shinytest2",
"Config/testthat/edition": "3",
"Encoding": "UTF-8",
"RoxygenNote": "7.3.3",
"Collate": "'globals.R' 'app-state.R' 'app_template.R' 'bind-cache.R' 'bind-event.R' 'bookmark-state-local.R' 'bookmark-state.R' 'bootstrap-deprecated.R' 'bootstrap-layout.R' 'conditions.R' 'map.R' 'utils.R' 'bootstrap.R' 'busy-indicators-spinners.R' 'busy-indicators.R' 'cache-utils.R' 'deprecated.R' 'devmode.R' 'diagnose.R' 'extended-task.R' 'fileupload.R' 'graph.R' 'reactives.R' 'reactive-domains.R' 'history.R' 'hooks.R' 'html-deps.R' 'image-interact-opts.R' 'image-interact.R' 'imageutils.R' 'input-action.R' 'input-checkbox.R' 'input-checkboxgroup.R' 'input-date.R' 'input-daterange.R' 'input-file.R' 'input-numeric.R' 'input-password.R' 'input-radiobuttons.R' 'input-select.R' 'input-slider.R' 'input-submit.R' 'input-text.R' 'input-textarea.R' 'input-utils.R' 'insert-tab.R' 'insert-ui.R' 'jqueryui.R' 'knitr.R' 'middleware-shiny.R' 'middleware.R' 'timer.R' 'shiny.R' 'mock-session.R' 'modal.R' 'modules.R' 'notifications.R' 'otel-attr-srcref.R' 'otel-collect.R' 'otel-enable.R' 'otel-error.R' 'otel-label.R' 'otel-reactive-update.R' 'otel-session.R' 'otel-shiny.R' 'otel-with.R' 'priorityqueue.R' 'progress.R' 'react.R' 'reexports.R' 'render-cached-plot.R' 'render-plot.R' 'render-table.R' 'run-url.R' 'runapp.R' 'serializers.R' 'server-input-handlers.R' 'server-resource-paths.R' 'server.R' 'shiny-options.R' 'shiny-package.R' 'shinyapp.R' 'shinyui.R' 'shinywrappers.R' 'showcase.R' 'snapshot.R' 'staticimports.R' 'tar.R' 'test-export.R' 'test-server.R' 'test.R' 'update-input.R' 'utils-lang.R' 'utils-tags.R' 'version_bs_date_picker.R' 'version_ion_range_slider.R' 'version_jquery.R' 'version_jqueryui.R' 'version_selectize.R' 'version_strftime.R' 'viewer.R'",
"NeedsCompilation": "no",
"Author": "Winston Chang [aut] (ORCID: <https://orcid.org/0000-0002-1576-2126>), Joe Cheng [aut], JJ Allaire [aut], Carson Sievert [aut, cre] (ORCID: <https://orcid.org/0000-0002-4958-2844>), Barret Schloerke [aut] (ORCID: <https://orcid.org/0000-0001-9986-114X>), Garrick Aden-Buie [aut] (ORCID: <https://orcid.org/0000-0002-7111-0077>), Yihui Xie [aut], Jeff Allen [aut], Jonathan McPherson [aut], Alan Dipert [aut], Barbara Borges [aut], Posit Software, PBC [cph, fnd] (ROR: <https://ror.org/03wc8by49>), jQuery Foundation [cph] (jQuery library and jQuery UI library), jQuery contributors [ctb, cph] (jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt), jQuery UI contributors [ctb, cph] (jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt), Mark Otto [ctb] (Bootstrap library), Jacob Thornton [ctb] (Bootstrap library), Bootstrap contributors [ctb] (Bootstrap library), Twitter, Inc [cph] (Bootstrap library), Prem Nawaz Khan [ctb] (Bootstrap accessibility plugin), Victor Tsaran [ctb] (Bootstrap accessibility plugin), Dennis Lembree [ctb] (Bootstrap accessibility plugin), Srinivasu Chakravarthula [ctb] (Bootstrap accessibility plugin), Cathy O'Connor [ctb] (Bootstrap accessibility plugin), PayPal, Inc [cph] (Bootstrap accessibility plugin), Stefan Petre [ctb, cph] (Bootstrap-datepicker library), Andrew Rowls [ctb, cph] (Bootstrap-datepicker library), Brian Reavis [ctb, cph] (selectize.js library), Salmen Bejaoui [ctb, cph] (selectize-plugin-a11y library), Denis Ineshin [ctb, cph] (ion.rangeSlider library), Sami Samhuri [ctb, cph] (Javascript strftime library), SpryMedia Limited [ctb, cph] (DataTables library), Ivan Sagalaev [ctb, cph] (highlight.js library), R Core Team [ctb, cph] (tar implementation from R)",
"Maintainer": "Carson Sievert <carson@posit.co>",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"signal": { "signal": {
"Package": "signal", "Package": "signal",
"Version": "1.8-1", "Version": "1.8-1",
@@ -2638,6 +2898,28 @@
"Maintainer": "Uwe Ligges <ligges@statistik.tu-dortmund.de>", "Maintainer": "Uwe Ligges <ligges@statistik.tu-dortmund.de>",
"Repository": "CRAN" "Repository": "CRAN"
}, },
"sourcetools": {
"Package": "sourcetools",
"Version": "0.1.7-1",
"Source": "Repository",
"Type": "Package",
"Title": "Tools for Reading, Tokenizing and Parsing R Code",
"Author": "Kevin Ushey",
"Maintainer": "Kevin Ushey <kevinushey@gmail.com>",
"Description": "Tools for the reading and tokenization of R code. The 'sourcetools' package provides both an R and C++ interface for the tokenization of R code, and helpers for interacting with the tokenized representation of R code.",
"License": "MIT + file LICENSE",
"Depends": [
"R (>= 3.0.2)"
],
"Suggests": [
"testthat"
],
"RoxygenNote": "5.0.1",
"BugReports": "https://github.com/kevinushey/sourcetools/issues",
"Encoding": "UTF-8",
"NeedsCompilation": "yes",
"Repository": "https://packagemanager.posit.co/cran/latest"
},
"ssh": { "ssh": {
"Package": "ssh", "Package": "ssh",
"Version": "0.9.4", "Version": "0.9.4",
@@ -3385,6 +3667,36 @@
"Maintainer": "Jeroen Ooms <jeroenooms@gmail.com>", "Maintainer": "Jeroen Ooms <jeroenooms@gmail.com>",
"Repository": "https://packagemanager.posit.co/cran/latest" "Repository": "https://packagemanager.posit.co/cran/latest"
}, },
"xtable": {
"Package": "xtable",
"Version": "1.8-4",
"Source": "Repository",
"Date": "2019-04-08",
"Title": "Export Tables to LaTeX or HTML",
"Authors@R": "c(person(\"David B.\", \"Dahl\", role=\"aut\"), person(\"David\", \"Scott\", role=c(\"aut\",\"cre\"), email=\"d.scott@auckland.ac.nz\"), person(\"Charles\", \"Roosen\", role=\"aut\"), person(\"Arni\", \"Magnusson\", role=\"aut\"), person(\"Jonathan\", \"Swinton\", role=\"aut\"), person(\"Ajay\", \"Shah\", role=\"ctb\"), person(\"Arne\", \"Henningsen\", role=\"ctb\"), person(\"Benno\", \"Puetz\", role=\"ctb\"), person(\"Bernhard\", \"Pfaff\", role=\"ctb\"), person(\"Claudio\", \"Agostinelli\", role=\"ctb\"), person(\"Claudius\", \"Loehnert\", role=\"ctb\"), person(\"David\", \"Mitchell\", role=\"ctb\"), person(\"David\", \"Whiting\", role=\"ctb\"), person(\"Fernando da\", \"Rosa\", role=\"ctb\"), person(\"Guido\", \"Gay\", role=\"ctb\"), person(\"Guido\", \"Schulz\", role=\"ctb\"), person(\"Ian\", \"Fellows\", role=\"ctb\"), person(\"Jeff\", \"Laake\", role=\"ctb\"), person(\"John\", \"Walker\", role=\"ctb\"), person(\"Jun\", \"Yan\", role=\"ctb\"), person(\"Liviu\", \"Andronic\", role=\"ctb\"), person(\"Markus\", \"Loecher\", role=\"ctb\"), person(\"Martin\", \"Gubri\", role=\"ctb\"), person(\"Matthieu\", \"Stigler\", role=\"ctb\"), person(\"Robert\", \"Castelo\", role=\"ctb\"), person(\"Seth\", \"Falcon\", role=\"ctb\"), person(\"Stefan\", \"Edwards\", role=\"ctb\"), person(\"Sven\", \"Garbade\", role=\"ctb\"), person(\"Uwe\", \"Ligges\", role=\"ctb\"))",
"Maintainer": "David Scott <d.scott@auckland.ac.nz>",
"Imports": [
"stats",
"utils"
],
"Suggests": [
"knitr",
"plm",
"zoo",
"survival"
],
"VignetteBuilder": "knitr",
"Description": "Coerce data to LaTeX and HTML tables.",
"URL": "http://xtable.r-forge.r-project.org/",
"Depends": [
"R (>= 2.10.0)"
],
"License": "GPL (>= 2)",
"Repository": "https://packagemanager.posit.co/cran/latest",
"NeedsCompilation": "no",
"Author": "David B. Dahl [aut], David Scott [aut, cre], Charles Roosen [aut], Arni Magnusson [aut], Jonathan Swinton [aut], Ajay Shah [ctb], Arne Henningsen [ctb], Benno Puetz [ctb], Bernhard Pfaff [ctb], Claudio Agostinelli [ctb], Claudius Loehnert [ctb], David Mitchell [ctb], David Whiting [ctb], Fernando da Rosa [ctb], Guido Gay [ctb], Guido Schulz [ctb], Ian Fellows [ctb], Jeff Laake [ctb], John Walker [ctb], Jun Yan [ctb], Liviu Andronic [ctb], Markus Loecher [ctb], Martin Gubri [ctb], Matthieu Stigler [ctb], Robert Castelo [ctb], Seth Falcon [ctb], Stefan Edwards [ctb], Sven Garbade [ctb], Uwe Ligges [ctb]",
"Encoding": "UTF-8"
},
"yaml": { "yaml": {
"Package": "yaml", "Package": "yaml",
"Version": "2.3.12", "Version": "2.3.12",

319
src/app.Rmd Normal file
View File

@@ -0,0 +1,319 @@
```{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
# All core functions are loaded from main.Rmd
# UI Definition
ui <- fluidPage(
titlePanel("Flight Trajectory Analysis - GUI"),
sidebarLayout(
sidebarPanel(
width = 3,
h4("OpenSky Credentials"),
textInput("client_id", "Client ID:", value = Sys.getenv('OPENSKY_CLIENT_ID')),
passwordInput("client_secret", "Client Secret:", value = Sys.getenv('OPENSKY_CLIENT_SECRET')),
hr(),
h4("Airport Selection"),
textInput("airport_code", "Airport ICAO Code:", value = "EDDF"),
sliderInput("hours_back", "Hours back from now:", min = 1, max = 12, value = 1),
actionButton("load_departures", "Load Departures", class = "btn-primary"),
hr(),
h4("Flight Selection"),
selectInput("selected_flight", "Select Flight:", choices = NULL),
actionButton("analyze_flight", "Analyze Selected Flight", class = "btn-success"),
hr(),
h4("Batch Analysis"),
numericInput("batch_size", "Number of flights to analyze:", value = 10, min = 2, max = 50),
actionButton("batch_analyze", "Run Batch Analysis", class = "btn-warning"),
hr(),
verbatimTextOutput("status_text")
),
mainPanel(
width = 9,
tabsetPanel(
id = "main_tabs",
tabPanel("Departures List",
h4("Available Departures"),
tableOutput("departures_table")
),
tabPanel("Single Flight Analysis",
fluidRow(
column(6, leafletOutput("route_plot", height = "400px")),
column(6, plotOutput("altitude_plot", height = "400px"))
),
fluidRow(
column(6, plotOutput("trajectory_plot", height = "400px")),
column(6,
h4("Trajectory Characteristics"),
tableOutput("characteristics_table"))
)
),
tabPanel("Statistical Analysis",
h4("Multiple Trajectory Statistics"),
tableOutput("stats_summary_table"),
hr(),
fluidRow(
column(12, plotOutput("boxplots", height = "500px"))
),
fluidRow(
column(12, plotOutput("density_plots", height = "500px"))
),
fluidRow(
column(12, plotOutput("histograms", height = "500px"))
)
),
tabPanel("Interpretation",
h4("Analysis Interpretation"),
verbatimTextOutput("interpretation_text")
)
)
)
)
)
# Server Logic
server <- function(input, output, session) {
# Reactive values to store data
rv <- reactiveValues(
creds = NULL,
departures = NULL,
departures_df = NULL,
current_route = NULL,
current_trj = NULL,
current_icao = NULL,
trajectory_stats_df = NULL
)
# Status message
status <- reactiveVal("Ready. Enter credentials and load departures.")
output$status_text <- renderText({
status()
})
# Load departures
observeEvent(input$load_departures, {
req(input$client_id, input$client_secret, input$airport_code)
status("Loading departures...")
tryCatch({
# Use getCredentials from main.Rmd
rv$creds <- getCredentials(
client_id = input$client_id,
client_secret = input$client_secret
)
time_now <- Sys.time()
rv$departures <- getAirportDepartures(
airport = input$airport_code,
startTime = time_now - hours(input$hours_back),
endTime = time_now,
credentials = rv$creds
)
if (length(rv$departures) > 0) {
# Create departures dataframe for display
departures_list <- lapply(seq_along(rv$departures), function(i) {
dep <- rv$departures[[i]]
data.frame(
Index = i,
ICAO24 = dep[["ICAO24"]] %||% NA,
Callsign = dep[["callsign"]] %||% NA,
Origin = dep[["estDepartureAirport"]] %||% NA,
Destination = dep[["estArrivalAirport"]] %||% NA,
DepartureTime = as.POSIXct(dep[["departure_time"]] %||% NA, origin = "1970-01-01"),
stringsAsFactors = FALSE
)
})
rv$departures_df <- do.call(rbind, departures_list)
# Update flight selection dropdown
choices <- setNames(
seq_along(rv$departures),
paste(rv$departures_df$ICAO24, "-", rv$departures_df$Callsign,
"(", rv$departures_df$Destination, ")")
)
updateSelectInput(session, "selected_flight", choices = choices)
status(paste("Loaded", length(rv$departures), "departures from", input$airport_code))
} else {
status("No departures found for the selected time period.")
}
}, error = function(e) {
status(paste("Error loading departures:", e$message))
})
})
# Display departures table
output$departures_table <- renderTable({
req(rv$departures_df)
rv$departures_df
})
# Analyze selected flight
observeEvent(input$analyze_flight, {
req(rv$departures, input$selected_flight, rv$creds)
status("Analyzing selected flight...")
tryCatch({
idx <- as.integer(input$selected_flight)
dep <- rv$departures[[idx]]
icao24 <- dep[["ICAO24"]]
dep_time <- dep[["departure_time"]]
rv$current_icao <- icao24
# Use getAircraftTrack from main.Rmd
route_df <- getAircraftTrack(icao24, dep_time, rv$creds)
if (is.null(route_df) || nrow(route_df) < 2) {
status(paste("No path data available for", icao24))
return()
}
rv$current_route <- 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
updateTabsetPanel(session, "main_tabs", selected = "Single Flight Analysis")
}, error = function(e) {
status(paste("Error analyzing flight:", e$message))
})
})
# Route plot
output$route_plot <- renderLeaflet({
req(rv$current_route)
createInteractiveMap(rv$current_route)
})
# Altitude plot
output$altitude_plot <- renderPlot({
req(rv$current_route)
plot(rv$current_route$time, rv$current_route$alt, type = "l", col = "red", lwd = 2,
main = paste("Altitude Profile of", rv$current_icao),
xlab = "Time (Unix)", ylab = "Altitude (m)")
})
# Trajectory plot
output$trajectory_plot <- renderPlot({
req(rv$current_trj)
plot(rv$current_trj, main = paste("Trajectory of", rv$current_icao))
})
# Characteristics table
output$characteristics_table <- renderTable({
req(rv$current_trj)
calculateTrajectoryStats(rv$current_trj, format = "table")
})
# Batch analysis
observeEvent(input$batch_analyze, {
req(rv$departures, rv$creds)
status("Running batch analysis...")
tryCatch({
all_trajectories <- list()
n_departures <- min(length(rv$departures), input$batch_size)
withProgress(message = 'Analyzing flights', value = 0, {
for (i in 1:n_departures) {
dep <- rv$departures[[i]]
icao24 <- dep[["ICAO24"]]
dep_time <- dep[["departure_time"]]
incProgress(1/n_departures, detail = paste("Processing", icao24))
if (is.null(dep_time)) next
# 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
}
Sys.sleep(0.3)
}
})
if (length(all_trajectories) > 0) {
rv$trajectory_stats_df <- do.call(rbind, all_trajectories)
status(paste("Batch analysis complete:", nrow(rv$trajectory_stats_df), "trajectories analyzed"))
updateTabsetPanel(session, "main_tabs", selected = "Statistical Analysis")
} else {
status("No trajectory data collected in batch analysis")
}
}, error = function(e) {
status(paste("Error in batch analysis:", e$message))
})
})
# Statistics summary table - use calculateStatsSummary from main.Rmd
output$stats_summary_table <- renderTable({
req(rv$trajectory_stats_df)
calculateStatsSummary(rv$trajectory_stats_df)
})
# Boxplots - use createBoxplots from main.Rmd
output$boxplots <- renderPlot({
req(rv$trajectory_stats_df)
createBoxplots(rv$trajectory_stats_df)
})
# Density plots - use createDensityPlots from main.Rmd
output$density_plots <- renderPlot({
req(rv$trajectory_stats_df)
createDensityPlots(rv$trajectory_stats_df)
})
# Histograms - use createHistograms from main.Rmd
output$histograms <- renderPlot({
req(rv$trajectory_stats_df)
createHistograms(rv$trajectory_stats_df)
})
# Interpretation text - use generateInterpretation from main.Rmd
output$interpretation_text <- renderText({
req(rv$trajectory_stats_df)
generateInterpretation(rv$trajectory_stats_df)
})
}
# Run the application
shinyApp(ui = ui, server = server)
```

View File

@@ -1,8 +1,9 @@
--- ---
title: "Topic 8" title: "Topic 8 - Flight Trajectory Analysis"
subtitle: "Erik Neller, Patrik Mišura, Lukas Adrion"
output: output:
pdf_document: default
html_document: default html_document: default
pdf_document: default
date: "`r Sys.Date()`" date: "`r Sys.Date()`"
--- ---
@@ -10,7 +11,7 @@ date: "`r Sys.Date()`"
knitr::opts_chunk$set(echo = TRUE) knitr::opts_chunk$set(echo = TRUE)
``` ```
```{r preamble, message=FALSE} ```{r preamble, message=FALSE, include=FALSE}
# Load Libraries # Load Libraries
library(dplyr) library(dplyr)
library(lubridate) library(lubridate)
@@ -21,74 +22,628 @@ library(dotenv)
library(httr) library(httr)
library(jsonlite) library(jsonlite)
library(trajr) library(trajr)
library(shiny)
library(leaflet)
``` ```
# Download flights ```{r opensky, include=FALSE}
```{r opensky} # Openskies API Functions
time_now <- Sys.time() time_now <- Sys.time()
creds <- getCredentials( creds <- getCredentials(
client_id = Sys.getenv('OPENSKY_CLIENT_ID'), client_id = Sys.getenv('OPENSKY_CLIENT_ID'),
client_secret = Sys.getenv('OPENSKY_CLIENT_SECRET')) client_secret = Sys.getenv('OPENSKY_CLIENT_SECRET'))
# get departures from Frankfurt airport # Get flights for a specific aircraft from OpenSky API
departures <- getAirportDepartures(airport = "EDDF", startTime = time_now - hours(1), endTime = time_now, credentials = creds )
getFlights <- function(icao, time, creds){ getFlights <- function(icao, time, creds){
flights <-getAircraftFlights(icao, startTime = time - days(1), endTime = time, credentials = creds ) flights <-getAircraftFlights(icao, startTime = time - days(1), endTime = time, credentials = creds )
return(flights) 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"]]))
# can get tracks for up to 30 days in the past # Get aircraft track from OpenSky API
response <-makeAuthenticatedRequest('tracks/all',query, creds) getAircraftTrack <- function(icao, time, creds) {
track_data <- fromJSON(content(response, as = "text", encoding = "UTF-8")) query <- list(icao24 = icao, time = as.numeric(time))
if (!is.null(track_data$path) && length(track_data$path) > 0) { 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) route_df <- as.data.frame(track_data$path)
colnames(route_df) <- c("time", "lat", "lon", "alt", "heading", "on_ground") colnames(route_df) <- c("time", "lat", "lon", "alt", "heading", "on_ground")
return(route_df)
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")
} }
``` return(NULL)
# GUI selection
```{r gui}
icaos <- lapply(departures, function(x) x[["ICAO24"]])
options <- unlist(icaos) # tcltk needs a character vector
# Create a GUI list selection
listSelect <- function(options){
selected_option <- NULL
tryCatch({
selected_option <- select.list(
title = "Select an aircraft",
choices = options,
preselect = NULL,
multiple = FALSE,
graphics = TRUE
)
}, error = function(w) {
message('No GUI available')
}
)
if (nzchar(selected_option)){
return(selected_option)
}
return(options[1])
} }
``` ```
```{r trajectory-functions, include=FALSE}
# Trajectory Conversion Functions
# Convert route to distance in meters
getRouteDistance <- function(route_df) {
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
return(list('x' = x_meters, 'y' = y_meters))
}
# Get time in seconds from start
getRouteTime <- function(route_df) {
return(route_df$time - route_df$time[1])
}
# Create trajr object from route
getTrajFromRoute <- function(route_df) {
meters <- getRouteDistance(route_df)
time <- getRouteTime(route_df)
trj <- TrajFromCoords(
data.frame(x = meters$x, y = meters$y, time = time),
xCol = "x", yCol = "y", timeCol = "time"
)
return(trj)
}
# Calculate trajectory characteristics
# Input: either route_df (data.frame with lat/lon) or trj (trajr object)
# format: "row" for batch analysis (one row per flight), "table" for single flight display
# FIXME for batch analysis: use the same aircraft
calculateTrajectoryStats <- function(input, icao = NULL, format = "row") {
# Determine if input is route_df or trj
if (inherits(input, "Trajectory")) {
trj <- input
} else {
trj <- getTrajFromRoute(input)
}
# Calculate all metrics
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 format based on use case
if (format == "table") {
# For single flight display (Parameter | Value)
return(data.frame(
Parameter = c(
"Duration (s)", "Duration (min)",
"Path Length (km)",
"Diffusion Distance (m)",
"Diffusion Distance (km)",
"Straightness Index",
"Mean Velocity (km/h)",
"Fractal Dimension"
),
Value = c(
round(duration, 2),
round(duration / 60, 2),
round(path_length / 1000, 2),
round(diffusion_distance, 2),
round(diffusion_distance / 1000, 2),
round(straightness, 4),
round(mean_velocity * 3.6, 2),
round(fractal_dim, 4)
)
))
} else {
# For batch analysis (one row per flight)
return(data.frame(
icao24 = icao,
diffusion_distance_km = diffusion_distance / 1000,
path_length_km = path_length / 1000,
straightness = straightness,
duration_min = duration / 60,
mean_velocity_kmh = mean_velocity * 3.6,
fractal_dimension = fractal_dim
))
}
}
# Calculate trajectory parameters for a single flight
calculate_trajectory_params <- function(icao, departure_time, creds) {
tryCatch({
route_df <- getAircraftTrack(icao, departure_time, creds)
if (is.null(route_df) || nrow(route_df) < 3) return(NULL)
return(calculateTrajectoryStats(route_df, icao = icao, format = "row"))
}, error = function(e) {
message("Error processing ", icao, ": ", e$message)
return(NULL)
})
}
```
```{r stat-functions, include=FALSE}
# Statistical Helper Functions
# Get parameter names and labels for trajectory statistics
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)])
}
```
```{r viz-functions, include=FALSE}
# Visualization Functions
# Create interactive map with leaflet
createInteractiveMap <- function(route) {
leaflet(route) %>%
addTiles() %>%
addPolylines(lng=~lon, lat=~lat, color="blue", weight=3, opacity=0.8) %>%
addCircleMarkers(
lng = ~lon[1],
lat = ~lat[1],
color = "green",
radius = 6,
popup = "Origin"
) %>%
addCircleMarkers(
lng = ~lon[nrow(route)],
lat = ~lat[nrow(route)],
color = "red",
radius = 6,
popup = "Destination"
)
}
# 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"
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")
}
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")
}
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")
}
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")
}
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
}
```
# Abstract
This project implements an R-based application for the retrieval, processing, and statistical analysis of aircraft trajectories. Flight data is obtained from the OpenSky Network API, transformed into analyzable trajectory objects using the `trajr` package, and subsequently characterized using established movement ecology metrics. The methodology enables quantitative comparison of flight paths through parameters such as path length, straightness index, and fractal dimension.
# Introduction
## Background
The analysis of movement trajectories constitutes a fundamental aspect of spatial data science, with applications ranging from animal behavior studies to transportation network optimization. In the context of aviation, trajectory analysis provides insights into flight efficiency, airspace utilization, and routing patterns.
## Objectives
The primary objectives of this project are:
1. **Data Acquisition**: Implement robust methods for retrieving real-time flight trajectory data from the OpenSky Network
2. **Trajectory Characterization**: Apply established metrics from movement ecology to quantify flight path properties
3. **Statistical Analysis**: Perform comparative analysis across multiple flights to identify patterns and distributions
## Theoretical Framework
The `trajr` package, originally developed for animal movement analysis, provides a comprehensive toolkit for trajectory characterization. Key metrics employed in this analysis include:
- **Path Length**: Total distance traveled along the trajectory
- **Diffusion Distance**: Euclidean displacement from origin to destination
- **Straightness Index**: Ratio of diffusion distance to path length (range 0-1)
- **Fractal Dimension**: Measure of path complexity (1 = straight line, approaching 2 = space-filling curve)
# Methodology
## Data Source
Flight trajectory data is obtained from the OpenSky Network, a community-based receiver network providing open access to air traffic surveillance data. The API provides:
- Aircraft state vectors (position, velocity, heading)
- Historical flight tracks
- Airport departure and arrival information
## Data Processing Pipeline
The analysis workflow consists of the following stages:
1. **Authentication**: Establish connection to OpenSky API using credentials
2. **Query Execution**: Retrieve departure information for specified airport and time window
3. **Track Retrieval**: Obtain detailed waypoint data for individual flights
4. **Coordinate Transformation**: Convert geographic coordinates to metric distances
5. **Trajectory Construction**: Create `trajr` trajectory objects for analysis
6. **Statistical Computation**: Calculate trajectory metrics and aggregate statistics
# Implementation
The following section will demonstrate the implementation of the methodology using R code snippets.
The full analysis is also available in the GUI-based Shiny application.
## Step 1: API Authentication
The `getCredentials()` function retrieves API credentials from environment variables, ensuring secure credential management.
```r
creds <- getCredentials(
client_id = Sys.getenv("OPENSKY_CLIENT_ID"),
client_secret = Sys.getenv("OPENSKY_CLIENT_SECRET")
)
```
## Step 2: Data Acquisition
Recent departures from Frankfurt Airport (ICAO: EDDF) are queried for a two-hour time window. This airport was selected due to its high traffic volume, ensuring sufficient data availability.
```{r demo-departures}
time_now <- Sys.time()
departures <- getAirportDepartures(
airport = "EDDF",
startTime = time_now - hours(2),
endTime = time_now - hours(1),
credentials = creds
)
cat("Departures retrieved:", length(departures), "\n")
```
## Step 3: Track Data Retrieval
The `getAircraftTrack()` function retrieves detailed waypoint data for individual aircraft. The function iterates through available departures until valid track data is obtained.
```{r demo-track}
route_df <- NULL
icao <- "N/A"
if (length(departures) > 0) {
for (i in seq_along(departures)) {
icao <- departures[[i]][["ICAO24"]]
dep_time <- departures[[i]][["departure_time"]]
route_df <- getAircraftTrack(icao, dep_time, creds)
if (!is.null(route_df) && nrow(route_df) >= 3) {
cat("Aircraft ICAO24:", icao, "\n")
cat("Track points acquired:", nrow(route_df), "\n")
break
}
}
}
if (is.null(route_df)) {
cat("No valid track data available\n")
}
```
## Step 4: Spatial Visualization
The geographic trajectory is visualized on an interactive map with leaflet using the `createInteractiveMap()` function. Green and red markers indicate departure and current/final position, respectively.
```{r demo-route-plot, fig.width=7, fig.height=5}
if (!is.null(route_df)) {
createInteractiveMap(route_df)
} else {
cat("Insufficient data for visualization\n")
}
```
## Step 5: Vertical Profile Analysis
The altitude profile reveals distinct flight phases: climb, cruise, and descent. This temporal representation provides insight into vertical movement patterns.
```{r demo-altitude-plot, fig.width=7, fig.height=4}
if (!is.null(route_df)) {
time_minutes <- (route_df$time - route_df$time[1]) / 60
plot(time_minutes, route_df$alt, type = "l", col = "red", lwd = 2,
main = paste("Altitude Profile -", icao),
xlab = "Elapsed Time (min)", ylab = "Barometric Altitude (m)")
grid()
} else {
cat("Insufficient data for altitude analysis\n")
}
```
## Step 6: Trajectory Object Construction
The `getTrajFromRoute()` function transforms geographic coordinates into a metric coordinate system and constructs a `trajr` trajectory object. This transformation is necessary for accurate distance calculations.
```{r demo-trajectory-plot, fig.width=7, fig.height=5}
if (!is.null(route_df)) {
trj <- getTrajFromRoute(route_df)
plot(trj, main = paste("Metric Trajectory -", icao))
cat("Trajectory object created with", nrow(trj), "waypoints\n")
} else {
cat("Insufficient data for trajectory construction\n")
}
```
## Step 7: Single Flight Characterization
The `calculateTrajectoryStats()` function computes comprehensive trajectory metrics. The table format provides a clear overview of individual flight characteristics.
```{r demo-stats-table}
if (!is.null(route_df)) {
stats_table <- calculateTrajectoryStats(route_df, icao = icao, format = "table")
knitr::kable(stats_table, caption = paste("Trajectory Metrics for Aircraft", icao))
} else {
cat("Insufficient data for statistical analysis\n")
}
```
## Step 8: Multi-Flight Data Collection
To enable statistical inference, trajectory data is collected for multiple flights. The algorithm attempts to retrieve valid track data for up to five departures in this example.
```{r demo-multiple-tracks}
flight_data <- list()
successful_flights <- 0
if (length(departures) > 0) {
max_attempts <- min(10, length(departures))
for (i in seq_len(max_attempts)) {
icao_temp <- departures[[i]][["ICAO24"]]
dep_time_temp <- departures[[i]][["departure_time"]]
route_df_temp <- getAircraftTrack(icao_temp, dep_time_temp, creds)
if (!is.null(route_df_temp) && nrow(route_df_temp) >= 3) {
stats <- calculateTrajectoryStats(route_df_temp, icao = icao_temp, format = "row")
if (!is.null(stats)) {
flight_data[[length(flight_data) + 1]] <- stats
successful_flights <- successful_flights + 1
cat("Flight", successful_flights, "| ICAO:", icao_temp,
"| Waypoints:", nrow(route_df_temp), "\n")
}
}
if (successful_flights >= 5) break
}
if (length(flight_data) > 0) {
all_flights_stats <- do.call(rbind, flight_data)
cat("\nSample size (n):", nrow(all_flights_stats), "flights\n")
} else {
all_flights_stats <- NULL
cat("No valid trajectories obtained\n")
}
} else {
all_flights_stats <- NULL
cat("No departure data available\n")
}
```
# Results
## Individual Flight Metrics
The following table presents computed metrics for all successfully analyzed flights.
```{r demo-all-stats-table}
if (!is.null(all_flights_stats)) {
display_stats <- all_flights_stats
display_stats$diffusion_distance_km <- round(display_stats$diffusion_distance_km, 2)
display_stats$path_length_km <- round(display_stats$path_length_km, 2)
display_stats$straightness <- round(display_stats$straightness, 4)
display_stats$duration_min <- round(display_stats$duration_min, 1)
display_stats$mean_velocity_kmh <- round(display_stats$mean_velocity_kmh, 1)
display_stats$fractal_dimension <- round(display_stats$fractal_dimension, 4)
knitr::kable(display_stats, caption = "Computed Trajectory Metrics",
col.names = c("ICAO24", "Displacement (km)", "Path Length (km)",
"Straightness", "Duration (min)", "Velocity (km/h)", "Fractal Dim."))
} else {
cat("No data available for tabulation\n")
}
```
## Descriptive Statistics
The `calculateStatsSummary()` function computes central tendency and dispersion measures for each trajectory parameter.
```{r demo-summary-stats}
if (!is.null(all_flights_stats) && nrow(all_flights_stats) >= 2) {
summary_stats <- calculateStatsSummary(all_flights_stats)
knitr::kable(summary_stats, caption = "Descriptive Statistics Summary")
} else {
cat("Minimum sample size (n >= 2) not met\n")
}
```
## Distribution Analysis: Boxplots
Boxplots provide a robust visualization of parameter distributions, displaying median, interquartile range, and potential outliers. The red diamond indicates the arithmetic mean.
```{r demo-boxplots, fig.width=10, fig.height=8}
if (!is.null(all_flights_stats) && nrow(all_flights_stats) >= 2) {
createBoxplots(all_flights_stats)
} else {
cat("Minimum sample size (n >= 2) not met\n")
}
```
## Distribution Analysis: Kernel Density Estimation
Density plots employ kernel density estimation to approximate the probability distribution of each parameter. Vertical lines indicate mean (red, dashed) and median (green, dotted).
```{r demo-density, fig.width=10, fig.height=8}
if (!is.null(all_flights_stats) && nrow(all_flights_stats) >= 3) {
createDensityPlots(all_flights_stats)
} else {
cat("Minimum sample size (n >= 3) not met for density estimation\n")
}
```
## Distribution Analysis: Histograms
Histograms with overlaid density curves provide an alternative visualization of parameter distributions.
```{r demo-histograms, fig.width=10, fig.height=8}
if (!is.null(all_flights_stats) && nrow(all_flights_stats) >= 3) {
createHistograms(all_flights_stats)
} else {
cat("Minimum sample size (n >= 3) not met for histogram analysis\n")
}
```
## Parameter Interpretation
The `generateInterpretation()` function provides contextual analysis of the computed trajectory metrics.
```{r demo-interpretation}
if (!is.null(all_flights_stats) && nrow(all_flights_stats) >= 2) {
interpretation <- generateInterpretation(all_flights_stats)
cat(interpretation)
} else {
cat("Minimum sample size (n >= 2) not met for interpretation\n")
}
```
# Discussion
## Key Findings
The trajectory analysis reveals several characteristics typical of commercial aviation:
1. **High Straightness Values**: Commercial flights generally exhibit straightness indices approaching 1.0, indicating efficient direct routing between waypoints.
2. **Low Fractal Dimension**: Values close to 1.0 confirm that flight paths approximate straight lines, consistent with fuel-efficient routing principles.
3. **Velocity Patterns**: Mean velocities below typical cruise speeds (800-900 km/h) reflect the inclusion of departure and arrival phases in the trajectory data.
## Limitations
- **Temporal Resolution**: Track data granularity varies based on ADS-B receiver coverage
- **Sample Size**: Statistical inference is limited by the number of available flights with complete track data
# Conclusion
This project demonstrates the successful application of movement ecology metrics to aviation trajectory analysis. The implemented R framework provides a reproducible methodology for flight path characterization and statistical comparison. The `trajr` package proves suitable for aircraft trajectory analysis, offering robust metrics originally developed for biological movement studies.
# References
- OpenSky Network: https://opensky-network.org/
- McLean, D.J. & Skowron Volponi, M.A. (2018). trajr: An R package for characterisation of animal trajectories. Ethology, 124(6), 440-448: https://CRAN.R-project.org/package=trajr