summaryrefslogtreecommitdiff
path: root/src/main/java/com/benburwell/planes/gui/aircraftmap/symbols/PlaneSymbol.java
blob: 3e6b51cd9191507280d71c2947769a26907f986a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package com.benburwell.planes.gui.aircraftmap.symbols;

import com.benburwell.planes.data.Aircraft;
import com.benburwell.planes.data.Position;
import com.benburwell.planes.gui.GraphicsTheme;
import com.benburwell.planes.gui.aircraftmap.AircraftMap;
import com.benburwell.planes.gui.aircraftmap.Drawable;
import com.benburwell.planes.gui.aircraftmap.GeoPoint;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.awt.geom.AffineTransform;

/**
 * @author ben
 */
public class PlaneSymbol extends GeoPoint implements Drawable {
    public final int TRIANGLE_HEIGHT = 6;
    public final int TRIANGLE_WIDTH = 4;
    public final int TEXT_OFFSET_X = 10;
    public final int TEXT_OFFSET_Y = 15;
    public final int MIN_COLOR_HEIGHT = 0;
    public final int MAX_COLOR_HEIGHT = 50000;
    public final long HISTORY_MILLIS = 5 * 60 * 1000;
    private String name;
    private double heading;
    private double speed;
    private double verticalRate;
    private List<Position> historyTrack = new ArrayList<>();

    public PlaneSymbol(Aircraft ac) {
        super(ac.getCurrentPosition());
        this.name = ac.getCallsign();
        this.heading = ac.getTrack();
        this.speed = ac.getGroundSpeed();
        this.verticalRate = ac.getVerticalRate();

        Date minTime = new Date(System.currentTimeMillis() - HISTORY_MILLIS);
        ac.getPositionHistory().stream()
            .filter(pos -> pos.getTimestamp().after(minTime))
            .forEach(pos -> historyTrack.add(pos));
    }

    public int getFlightLevel() {
        return (int) this.getAltitude() / 100;
    }

    public Color getPlaneColor() {
        Color minColor = GraphicsTheme.Colors.RED;
        Color maxColor = GraphicsTheme.Colors.GREEN;

        float[] minHsb = Color.RGBtoHSB(minColor.getRed(), minColor.getGreen(), minColor.getBlue(), null);
        float[] maxHsb = Color.RGBtoHSB(maxColor.getRed(), maxColor.getGreen(), maxColor.getBlue(), null);

        float minHue = minHsb[0];
        float maxHue = maxHsb[0];
        float minSat = minHsb[1];
        float maxSat = maxHsb[1];
        float minBright = minHsb[2];
        float maxBright = maxHsb[2];

        double planePosition = (this.getAltitude() / (MAX_COLOR_HEIGHT - MIN_COLOR_HEIGHT)) + MIN_COLOR_HEIGHT;
        float huePosition = (float) (planePosition * (maxHue - minHue) + minHue);
        float satPosition = (float) (planePosition * (maxSat - minSat) + minSat);
        float brightPosition = (float) (planePosition * (maxBright - minBright) + minBright);

        Color c = Color.getHSBColor(huePosition, satPosition, brightPosition);
        return c;
    }

    public double getAngle() {
        return Math.toRadians(this.heading);
    }

    public double getSpeed() {
        return this.speed;
    }

    public void drawTriangle(Graphics2D ctx, int x, int y, int predictionLength) {
        AffineTransform at = new AffineTransform();
        at.setToRotation(this.getAngle(), x, y);
        ctx.setTransform(at);
        int[] xs = new int[]{ x - TRIANGLE_WIDTH, x, x + TRIANGLE_WIDTH, x - TRIANGLE_WIDTH };
        int[] ys = new int[]{ y + TRIANGLE_HEIGHT, y - TRIANGLE_HEIGHT, y + TRIANGLE_HEIGHT, y + TRIANGLE_HEIGHT };
        ctx.fillPolygon(xs, ys, 4);
        ctx.drawLine(x, y, x, y - predictionLength);
    }

    public String getVerticalRateIndicator() {
        if (this.verticalRate > 0) {
            return "\u2191"; // ↑
        } else if (this.verticalRate < 0) {
            return "\u2193"; // ↓
        }
        return "";
    }

    public String getDisplayName() {
        if (this.name == null || this.name.isEmpty()) {
            return "-----";
        }
        return this.name;
    }

    public int getPredictionLength(double pixelsPerNauticalMile) {
        return (int) (this.speed / 60.0 * pixelsPerNauticalMile);
    }

    public void drawOn(Graphics g, AircraftMap map) {
        if (this.shouldDrawOn(map)) {
            int x = this.getX(map);
            int y = this.getY(map);

            // draw the plane dot
            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(this.getPlaneColor());
            int predictedTrack = this.getPredictionLength(map.getPixelsPerNauticalMile());
            this.drawTriangle(g2d, x, y, predictedTrack);
            g2d.dispose();

            // draw the history track
            g.setColor(GraphicsTheme.Colors.BASE_4);
            this.historyTrack.forEach(pos -> {
                GeoPoint p = new GeoPoint(pos);
                g.fillRect(p.getX(map) - 2, p.getY(map) - 2, 4, 4);
            });

            // draw the name of the plane
            g.setColor(GraphicsTheme.Colors.BASE_5);
            g.drawString(this.getDisplayName(), x + TEXT_OFFSET_X, y + TEXT_OFFSET_Y);
            String infoString = String.format("%d%s %.1f", this.getFlightLevel(), this.getVerticalRateIndicator(), this.getSpeed());
            g.drawString(infoString,  x + TEXT_OFFSET_X, y + TEXT_OFFSET_Y + g.getFontMetrics().getHeight());
        }
    }
}