{"id":23164,"date":"2026-04-19T07:37:25","date_gmt":"2026-04-19T05:37:25","guid":{"rendered":"http:\/\/blog.wenzlaff.de\/?p=23164"},"modified":"2026-04-19T11:43:11","modified_gmt":"2026-04-19T09:43:11","slug":"flugmonitor-echtzeit-flugverkehr-mit-java-und-opensky-visualisieren","status":"publish","type":"post","link":"http:\/\/blog.wenzlaff.de\/?p=23164","title":{"rendered":"FlugMonitor \u2013 Echtzeit-Flugverkehr mit Java und OpenSky visualisieren"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/flugmonitor-gui.png\" alt=\"\" width=\"1222\" height=\"1798\" \/><\/p>\n<p>Wer kennt das nicht: Man schaut in den Himmel, sieht ein Flugzeug und fragt sich, wo es gerade herkommt und in welcher H\u00f6he es fliegt. Mit dem FlugMonitor l\u00e4sst sich genau diese Frage beantworten \u2013 und zwar f\u00fcr alle Flugzeuge auf der Welt gleichzeitig. In diesem Artikel zeige ich, wie ich eine Java-Swing-Anwendung gebaut habe, die Echtzeit-Daten des OpenSky Network abruft, auswertet und \u00fcbersichtlich darstellt.<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nWas ist der FlugMonitor?<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Der FlugMonitor ist eine Desktop-Anwendung, die ich mit Java 21 und Swing entwickelt habe. Swing ist das klassische GUI-Framework von Java \u2013 also das Werkzeugkasten, mit dem man Fenster, Tabellen und Buttons baut. Die Anwendung zeigt auf einen Blick, wie viele Flugzeuge sich gerade weltweit in welchem H\u00f6henbereich befinden. Die Daten kommen dabei in Echtzeit direkt vom OpenSky Network, das Flugzeugpositionen aus aller Welt sammelt und kostenlos zur Verf\u00fcgung stellt.<\/p>\n<p>Das Besondere: Die Anwendung fragt bei jedem Klick auf &#8222;Aktualisieren&#8220; live die OpenSky-API ab und wertet tausende Flugzeugpositionen in Millisekunden aus. Das Ergebnis landet sofort in einer \u00fcbersichtlichen Tabelle und einem farbigen Balkendiagramm.<\/p>\n<p><!--more--><\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nWas ist das OpenSky Network?<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Das OpenSky Network (opensky-network.org) ist ein internationales Forschungsprojekt, das Flugzeugdaten per ADS-B empf\u00e4ngt. ADS-B steht f\u00fcr &#8222;Automatic Dependent Surveillance\u2013Broadcast&#8220; \u2013 ein System, bei dem moderne Flugzeuge automatisch ihre Position, Geschwindigkeit und H\u00f6he per Funksignal ausstrahlen. Tausende freiwillige Helfer auf der ganzen Welt haben ADS-B-Empf\u00e4nger aufgestellt und senden ihre Empfangsdaten kontinuierlich an das OpenSky-Netzwerk.<\/p>\n<p>F\u00fcr Entwickler stellt OpenSky eine kostenlose REST-API bereit. Eine Java-Bibliothek macht den Zugriff besonders einfach: Mit wenigen Zeilen Code holt man sich den aktuellen Zustand aller sichtbaren Flugzeuge als Liste von &#8222;StateVector&#8220;-Objekten. Jedes StateVector enth\u00e4lt unter anderem:<\/p>\n<p>&#8211; Rufzeichen des Flugzeugs (ICAO24-Transpondercode)<br \/>\n&#8211; Geografische Position (L\u00e4ngen- und Breitengrad)<br \/>\n&#8211; Geod\u00e4tische H\u00f6he in Metern<br \/>\n&#8211; Geschwindigkeit und Kursrichtung<br \/>\n&#8211; Ob das Flugzeug am Boden ist (onGround-Flag)<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nDie Benutzeroberfl\u00e4che im \u00dcberblick<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Beim Start der Anwendung \u00f6ffnet sich ein Fenster mit drei Bereichen:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/flugmonitor-gui.png\" alt=\"\" width=\"1222\" height=\"1798\" \/><\/p>\n<p>1. Zwei Schaltfl\u00e4chen oben:<br \/>\n   &#8211; &#8222;Aktualisieren&#8220; (blau): Holt die neuesten Daten vom OpenSky-Server.<br \/>\n   &#8211; &#8222;Kopieren&#8220; (gr\u00fcn): \u00dcbertr\u00e4gt die gesamte Tabelle als formatierten Text<br \/>\n     in die Zwischenablage, zum Beispiel f\u00fcr Berichte oder E-Mails.<\/p>\n<p>2. Eine Tabelle in der Mitte mit zwei Spalten:<br \/>\n   &#8211; &#8222;Flugbereich in Meter&#8220;: Die H\u00f6henkategorie, z. B. &#8222;11.000 \u2013 11.500&#8220;<br \/>\n   &#8211; &#8222;Flugh\u00f6he in Meter&#8220;: Die Anzahl der Flugzeuge in diesem Bereich<\/p>\n<p>   Die Tabelle hat 18 Zeilen f\u00fcr verschiedene H\u00f6henb\u00e4nder, eine<br \/>\n   hervorgehobene Summenzeile am Ende sowie Zebrastreifen zur besseren<br \/>\n   Lesbarkeit.<\/p>\n<p>3. Ein Balkendiagramm unten:<br \/>\n   Das Diagramm zeigt dieselben Daten visuell. Jeder Balken entspricht einer<br \/>\n   H\u00f6henkategorie. Die Farben verlaufen von Graugr\u00fcn (am Boden \/ &#8222;Parken&#8220;)<br \/>\n   \u00fcber T\u00fcrkis und Blau (mittlere Flugh\u00f6hen) bis zu Violett (Reiseflugh\u00f6hen<br \/>\n   um 11.000 Meter) und zur\u00fcck zu Grau (Stratosph\u00e4re \u00fcber 13.000 Meter).<br \/>\n   So erkennt man auf den ersten Blick, welche H\u00f6hen am st\u00e4rksten genutzt<br \/>\n   werden.<\/p>\n<p>Unten in der Statusleiste steht nach jedem Abruf der genaue Zeitstempel,<br \/>\nz. B. &#8222;Zeitpunkt: So. 19.04.2026 06:33:30&#8220;. <\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nDie H\u00f6henbereiche \u2013 was bedeuten sie?<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Die 18 Kategorien in der Tabelle folgen einer fachlichen Logik:<\/p>\n<p>Parken (0 m): Flugzeuge, die am Boden stehen. Das erkennt die Anwendung<br \/>\nam &#8222;onGround&#8220;-Flag im StateVector oder daran, dass keine H\u00f6henangabe<br \/>\nvorliegt. An einem normalen Tag sind das mehrere Hundert Maschinen weltweit,<br \/>\ndie von OpenSky trotzdem erfasst werden (z. B. auf belebten Rollfeldern).<\/p>\n<p>1 bis 500 m: Start- und Landephase. Flugzeuge steigen kurz nach dem Abheben<br \/>\nsehr steil, daher ist diese Kategorie weniger stark besetzt als man erwarten<br \/>\nw\u00fcrde.<\/p>\n<p>500 bis 5.000 m: Steig- und Sinkflug. Hier befinden sich Maschinen auf dem<br \/>\nWeg zu ihrer Reiseflugh\u00f6he sowie Propellermaschinen und Hubschrauber auf<br \/>\nk\u00fcrzeren Strecken.<\/p>\n<p>5.000 bis 9.000 m: Unterer Reiseflugbereich. Kleinere Verkehrsflugzeuge und<br \/>\nRegionalfl\u00fcge nutzen diesen Bereich.<\/p>\n<p>9.000 bis 13.000 m: Oberer Reiseflugbereich. Das ist die Welt der gro\u00dfen<br \/>\nVerkehrsflugzeuge wie Airbus A320, Boeing 737 oder A380. Der deutliche<br \/>\nGipfel bei 11.000 bis 11.500 Metern entspricht der typischen Reiseflugh\u00f6he<br \/>\n(FL350 bis FL380 in der Luftfahrt-Sprache). Hier fliegen die meisten<br \/>\nLangstreckenmaschinen.<\/p>\n<p>\u00dcber 13.000 m: Wenige spezialisierte Flugzeuge, z. B. bestimmte<br \/>\nBusinessjets oder Milit\u00e4rflugzeuge.<\/p>\n<p>Das Diagramm auf dem Screenshot zeigt sch\u00f6n den charakteristischen Einbruch<br \/>\nbei 5.000 bis 7.000 Metern (kaum Flugzeuge in diesem Zwischenbereich) und<br \/>\nden deutlichen Gipfel bei 11.000 bis 11.500 Metern \u2013 der klassischen<br \/>\nReiseflugh\u00f6he moderner Verkehrsflugzeuge.<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nTechnischer Aufbau \u2013 Schritt f\u00fcr Schritt erkl\u00e4rt<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p><strong>Die Projektstruktur<\/strong><\/p>\n<p>Das Projekt besteht f\u00fcr die Gui aus einer einzigen Java-Datei: FlugMonitorSwing.java.<\/p>\n<p>Maven verwaltet die Abh\u00e4ngigkeiten. <\/p>\n<p>Die Hauptklasse hei\u00dft FlugMonitorSwing und erbt von JFrame \u2013 dem Standard-Fenster in Swing. Innerhalb der Klasse gibt es zwei wichtige innere Klassen: DiagramPanel f\u00fcr das Balkendiagramm und ZebraRenderer f\u00fcr die Tabellendarstellung.<\/p>\n<p><strong>Datenabruf mit SwingWorker<\/strong><\/p>\n<p>In Java gibt es eine wichtige Regel: Alles, was die Benutzeroberfl\u00e4che betrifft, muss im sogenannten Event Dispatch Thread (EDT) laufen. Netzwerk- anfragen blockieren aber den Thread, solange sie laufen \u2013 das w\u00fcrde das Fenster einfrieren.<\/p>\n<p>Die L\u00f6sung hei\u00dft SwingWorker. Das ist eine Klasse, die einen Hintergrundthread startet, die Arbeit erledigt (hier: Netzwerkabruf) und danach sauber in den EDT zur\u00fcckwechselt, um die Oberfl\u00e4che zu aktualisieren:<\/p>\n<pre class=\"lang:default decode:true \" >   new SwingWorker&lt;int[], Void&gt;() {\r\n        @Override\r\n        protected int[] doInBackground() throws IOException {\r\n            \/\/ L\u00e4uft im Hintergrund \u2013 Netzwerkabruf\r\n            OpenSkyStates states = OpenSkyApi\r\n                .getOpenSkyApiInstance()\r\n                .getStates(0, null);\r\n            return berechneVerteilung(states.getStates());\r\n        }\r\n\r\n        @Override\r\n        protected void done() {\r\n            \/\/ L\u00e4uft im EDT \u2013 Oberfl\u00e4che aktualisieren\r\n            int[] z = get();\r\n            aktualisiereTabelle(z);\r\n            diagramPanel.setDaten(z);\r\n        }\r\n    }.execute();<\/pre>\n<p>W\u00e4hrend der Abruf l\u00e4uft, sind die Buttons deaktiviert (setEnabled(false)), damit man nicht versehentlich einen zweiten Abruf startet. Nach dem Abruf werden sie wieder aktiviert.<\/p>\n<p><strong>Verteilung berechnen<\/strong><\/p>\n<p>Die Methode berechneVerteilung() geht alle StateVector-Objekte durch und entscheidet f\u00fcr jedes Flugzeug, in welche H\u00f6henkategorie es geh\u00f6rt:<\/p>\n<pre class=\"lang:default decode:true \" >  private int[] berechneVerteilung(Collection&lt;StateVector&gt; vektoren) {\r\n        int[] z = new int[BEREICHE.size()];\r\n        for (StateVector sv : vektoren) z[bestimmeIndex(sv)]++;\r\n        return z;\r\n    }\r\n\r\n    private int bestimmeIndex(StateVector sv) {\r\n        if (Boolean.TRUE.equals(sv.isOnGround())\r\n                || sv.getGeoAltitude() == null) return 0;\r\n        double h = sv.getGeoAltitude();\r\n        for (int i = 1; i &lt; BEREICHE.size(); i++) {\r\n            Bereich b = BEREICHE.get(i);\r\n            if (h &gt;= b.lower &amp;&amp; h &lt; b.upper) return i;\r\n        }\r\n        return BEREICHE.size() - 1;\r\n    }<\/pre>\n<p>Die Bereiche sind als unver\u00e4nderliche Java-Records definiert:<\/p>\n<pre class=\"lang:default decode:true \" > private record Bereich(String label, int lower, int upper) {}<\/pre>\n<p>Records sind ein modernes Java-Feature (ab Java 16), das kompakte Datenklassen ohne Boilerplate erm\u00f6glicht.<\/p>\n<p><strong>Das Balkendiagramm mit Graphics2D<\/strong><\/p>\n<p>F\u00fcr das Diagramm wurde bewusst keine externe Bibliothek (wie JFreeChart) eingesetzt. Stattdessen zeichnet die innere Klasse DiagramPanel alles selbst mit Java2D, dem eingebauten Grafiksystem von Java.<\/p>\n<p>Jede Swing-Komponente kann die Methode paintComponent(Graphics g) \u00fcberschreiben und dann beliebige Grafiken zeichnen. Der Schl\u00fcssel ist das Casting auf Graphics2D, das erweiterte M\u00f6glichkeiten bietet:<\/p>\n<pre class=\"lang:default decode:true \" >    @Override\r\n    protected void paintComponent(Graphics g) {\r\n        super.paintComponent(g);\r\n        Graphics2D g2 = (Graphics2D) g.create();\r\n        g2.setRenderingHint(\r\n            RenderingHints.KEY_ANTIALIASING,\r\n            RenderingHints.VALUE_ANTIALIAS_ON);\r\n        \/\/ ... Diagramm zeichnen ...\r\n        g2.dispose();\r\n    }<\/pre>\n<p>Antialiasing sorgt daf\u00fcr, dass Kanten und Schrift weich und nicht pixelig erscheinen. Wichtig: g.create() erzeugt eine Kopie des Graphics-Objekts, die am Ende mit dispose() sauber freigegeben wird.<\/p>\n<p>Die Balken werden als RoundRectangle2D gezeichnet \u2013 ein Rechteck mit abgerundeten Ecken:<\/p>\n<pre class=\"lang:default decode:true \" >    g2.fill(new RoundRectangle2D.Float(mL, y, effLen, barH, 4, 4));<\/pre>\n<p>Dar\u00fcber kommt ein halbtransparenter wei\u00dfer Streifen (Alpha-Wert 35 von 255), der einen Glanzeffekt imitiert:<\/p>\n<pre class=\"lang:default decode:true \" >    g2.setColor(new Color(255, 255, 255, 35));\r\n    g2.fillRect(mL, y, effLen, Math.max(barH \/ 3, 1));<\/pre>\n<p><strong>Farben nach H\u00f6he<\/strong><\/p>\n<p>Die 18 Farben f\u00fcr die Balken wurden manuell als Hex-Werte festgelegt. Der Verlauf folgt einer inhaltlichen Logik: Graugr\u00fcn f\u00fcr geparkte Flugzeuge am Boden, \u00fcber lebendiges Gr\u00fcn (Niedrigflug), T\u00fcrkis und Blau (mittlere H\u00f6hen), bis zu sattem Violett f\u00fcr den Reiseflugbereich, und zur\u00fcck zu ged\u00e4mpftem Blaugrau f\u00fcr die Stratosph\u00e4re:<\/p>\n<pre class=\"lang:default decode:true \" > private static final Color[] FARBEN = {\r\n        new Color(120, 140, 100),  \/\/ Parken \u2013 graugr\u00fcn\r\n        new Color( 72, 168,  72),  \/\/ 1-500 m \u2013 kr\u00e4ftiges Gr\u00fcn\r\n        \/\/ ... weitere Stufen ...\r\n        new Color( 98,  48, 188),  \/\/ 10.500-11.000 \u2013 Violett (Gipfel)\r\n        \/\/ ...\r\n        new Color( 90,  85, 120),  \/\/ \u00fcber 13.000 \u2013 Grauviolett\r\n    };<\/pre>\n<p><strong>Tabelle mit Zebrastreifen<\/strong><\/p>\n<p>Die Tabelle verwendet einen eigenen ZebraRenderer, der von DefaultTableCellRenderer erbt. Je nach Zeilennummer wird die Hintergrundfarbe gewechselt. Die Summenzeile erh\u00e4lt eine Sonderbehandlung:<\/p>\n<pre class=\"lang:default decode:true \" >   @Override\r\n    public Component getTableCellRendererComponent(...) {\r\n        super.getTableCellRendererComponent(...);\r\n        if (!isSelected) {\r\n            boolean istSumme = row == table.getRowCount() - 1;\r\n            setFont(getFont().deriveFont(\r\n                istSumme ? Font.BOLD : Font.PLAIN));\r\n            setBackground(istSumme ? SUMME :\r\n                (row % 2 == 0 ? GERADE : UNGERADE));\r\n        }\r\n        return this;\r\n    }\r\n\r\n    \/\/ Farben:\r\n    GERADE   = new Color(240, 246, 252)  \/\/ helles Blaugrau\r\n    UNGERADE = Color.WHITE\r\n    SUMME    = new Color(210, 232, 205)  \/\/ helles Gr\u00fcn\r\n\r\n<\/pre>\n<p><strong>Zwischenablage<\/strong><\/p>\n<p>Der &#8222;Kopieren&#8220;-Button baut einen formatierten String aus dem TableModel:<\/p>\n<pre class=\"lang:default decode:true \" >  for (int i = 0; i &lt; tabellenModell.getRowCount(); i++)\r\n        sb.append(String.format(\"%-24s ; %s%n\",\r\n            tabellenModell.getValueAt(i, 0),\r\n            tabellenModell.getValueAt(i, 1)));<\/pre>\n<p>Dieser String wird dann per Toolkit in die System-Zwischenablage gelegt:<\/p>\n<pre class=\"lang:default decode:true \" >    Toolkit.getDefaultToolkit().getSystemClipboard()\r\n           .setContents(new StringSelection(sb.toString()), null);<\/pre>\n<p>Damit kann der Inhalt direkt in Excel, eine E-Mail oder einen Texteditor eingef\u00fcgt werden.<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nBuild und Start<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Das Projekt nutzt Maven als Build-System. Die pom.xml definiert Java 21 als Zielversion und bindet das Maven Shade Plugin ein, das alle<br \/>\nAbh\u00e4ngigkeiten in eine einzige ausf\u00fchrbare JAR-Datei b\u00fcndelt (Fat-JAR):<\/p>\n<pre class=\"lang:default decode:true \" >\r\n    mvn package\r\n    java -jar target\/flug-monitor-swing-2.0.0.jar<\/pre>\n<p>Alternativ kann die Anwendung direkt aus der IDE (IntelliJ IDEA, Eclipse) gestartet werden, indem man FlugMonitorSwing.main() ausf\u00fchrt.<\/p>\n<p>Voraussetzung ist eine Java-21-Installation sowie eine Internetverbindung zum OpenSky-Server. Die OpenSky-API ist ohne Registrierung nutzbar, unterliegt aber einem Rate-Limit (ca. 10 Anfragen pro Minute f\u00fcr anonyme Nutzer). Wer mehr Anfragen ben\u00f6tigt, kann sich kostenlos registrieren und erh\u00e4lt dann ein erh\u00f6htes Kontingent.<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nWas zeigt der aktuelle Screenshot?<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Der Screenshot entstand am Sonntag, dem 19. April 2026 um 06:33:30 Uhr.<br \/>\nZu diesem fr\u00fchen Sonntagmorgen-Zeitpunkt waren 4.821 Flugzeuge weltweit<br \/>\nvon OpenSky erfasst. Die Verteilung zeigt das typische Muster:<\/p>\n<p>&#8211; 662 Flugzeuge stehen am Boden (&#8222;Parken&#8220;) \u2013 wartende Maschinen auf<br \/>\n  Flugh\u00e4fen in Asien, Europa und Amerika.<\/p>\n<p>&#8211; Der H\u00f6henbereich 11.000 \u2013 11.500 m ist mit 498 Flugzeugen am<br \/>\n  st\u00e4rksten besetzt. Das sind \u00fcberwiegend Langstreckenmaschinen auf<br \/>\n  Interkontinentalrouten (z. B. Nordatlantik oder Transpazifik).<\/p>\n<p>&#8211; Zwischen 5.000 und 7.000 Metern gibt es einen deutlichen Einbruch<br \/>\n  (136 und 134 Flugzeuge). Diese &#8222;L\u00fccke&#8220; ist charakteristisch: Flugzeuge<br \/>\n  durchqueren diesen Bereich nur kurz beim Steigen und Sinken, niemand<br \/>\n  fliegt dort dauerhaft.<\/p>\n<p>&#8211; Der zweite Gipfel bei 10.500 \u2013 11.000 m (380 Flugzeuge) und<br \/>\n  11.500 \u2013 12.000 m (435 Flugzeuge) best\u00e4tigt, dass der gesamte Bereich<br \/>\n  zwischen FL320 und FL390 der Hauptverkehrsweg der zivilen Luftfahrt ist.<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nM\u00f6gliche Erweiterungen<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Der FlugMonitor l\u00e4sst sich auf viele Weisen ausbauen:<\/p>\n<p>Filterung nach Region: Die OpenSky-API erlaubt es, einen geografischen Ausschnitt (Bounding Box) zu \u00fcbergeben. So k\u00f6nnte man nur Flugzeuge \u00fcber Deutschland oder Europa anzeigen.<\/p>\n<p>Automatische Aktualisierung: Ein javax.swing.Timer k\u00f6nnte alle 60 Sekunden automatisch aktualisieren, ohne dass der Nutzer auf den Button klicken muss.<\/p>\n<p>Historische Daten: OpenSky bietet auch eine History-API. Damit lie\u00dfe sich der Tagesverlauf darstellen \u2013 morgens weniger, tags\u00fcber mehr Flugzeuge.<\/p>\n<p>Karte: Mit einem Kartenbibliothek (z. B. JXMapViewer oder einer eingebetteten WebView) k\u00f6nnten die Flugzeuge auf einer Weltkarte als Punkte dargestellt werden.<\/p>\n<p>CSV-Export: Neben dem Kopieren-Button k\u00f6nnte ein Export-Button eine CSV-Datei schreiben, die sich direkt in Excel \u00f6ffnen l\u00e4sst.<\/p>\n<p>Dark Mode: Swing unterst\u00fctzt mit modernen Look-and-Feels wie FlatLaf einen echten Dark Mode, der die dunkle DiagramPanel-Optik nahtlos in die gesamte Oberfl\u00e4che integrieren w\u00fcrde.<\/p>\n<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br \/>\nFazit<br \/>\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<\/p>\n<p>Der FlugMonitor zeigt, wie viel man mit purem Java und ohne externe Grafikbibliotheken erreichen kann. Die Kombination aus SwingWorker (f\u00fcr reaktive UI), Graphics2D (f\u00fcr das Diagramm) und der OpenSky-API (f\u00fcr Echtzeit-Daten) ergibt eine vollst\u00e4ndige Desktop-Anwendung, die sich in wenigen Minuten bauen und starten l\u00e4sst.<\/p>\n<p>Der vollst\u00e4ndige Quellcode und die Maven-Konfiguration stehen auf <a href=\"https:\/\/gitlab.com\/IT-Berater\/twflightaltitudemonitor\">GitLab<\/a> zum Download bereit.<\/p>\n<p>Wer Fragen oder Anmerkungen hat, kann diese gerne in den Kommentaren hinterlassen \u2013 ich freue mich \u00fcber jede R\u00fcckmeldung!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-scaled.png\" alt=\"\" width=\"2560\" height=\"1755\" class=\"aligncenter size-full wp-image-23174\" srcset=\"http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-scaled.png 2560w, http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-300x206.png 300w, http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-1024x702.png 1024w, http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-768x527.png 768w, http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-1536x1053.png 1536w, http:\/\/blog.wenzlaff.de\/wp-content\/uploads\/2026\/04\/wenzlaff.de-2026-04-19-um-11.42.34-2048x1404.png 2048w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wer kennt das nicht: Man schaut in den Himmel, sieht ein Flugzeug und fragt sich, wo es gerade herkommt und in welcher H\u00f6he es fliegt. Mit dem FlugMonitor l\u00e4sst sich genau diese Frage beantworten \u2013 und zwar f\u00fcr alle Flugzeuge auf der Welt gleichzeitig. In diesem Artikel zeige ich, wie ich eine Java-Swing-Anwendung gebaut habe, &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/blog.wenzlaff.de\/?p=23164\" class=\"more-link\"><span class=\"screen-reader-text\">\u201eFlugMonitor \u2013 Echtzeit-Flugverkehr mit Java und OpenSky visualisieren\u201c <\/span>weiterlesen<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[220,3897,5,6180,4931,79,2713],"tags":[1792,6302,1861,6300,680,2178,6299,288,66,2840,1363,6301],"class_list":["post-23164","post","type-post","status-publish","format-standard","hentry","category-anleitung","category-java-programmierung","category-java","category-java-21","category-planespotting","category-programmierung","category-statistik","tag-ads-b","tag-desktop-anwendung","tag-flugverkehr","tag-graphics2d","tag-gui","tag-java","tag-java21","tag-kleinhirn-eu","tag-maven","tag-opensky","tag-swing","tag-swingworker"],"_links":{"self":[{"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=\/wp\/v2\/posts\/23164","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=23164"}],"version-history":[{"count":2,"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=\/wp\/v2\/posts\/23164\/revisions"}],"predecessor-version":[{"id":23176,"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=\/wp\/v2\/posts\/23164\/revisions\/23176"}],"wp:attachment":[{"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=23164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=23164"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.wenzlaff.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=23164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}