]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/ProfileChart.java
Version 8, September 2009
[GpsPrune.git] / tim / prune / gui / ProfileChart.java
1 package tim.prune.gui;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Graphics;
6 import java.awt.event.MouseEvent;
7
8 import tim.prune.I18nManager;
9 import tim.prune.data.Altitude;
10 import tim.prune.data.AltitudeRange;
11 import tim.prune.data.Track;
12 import tim.prune.data.TrackInfo;
13
14 /**
15  * Chart component for the profile display
16  */
17 public class ProfileChart extends GenericChart
18 {
19         private double _xScaleFactor = 0.0;
20         private static final int[] ALTITUDE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50};
21         private static final Color COLOR_LINES       = Color.GRAY;
22         private static final Color COLOR_ALT_BARS    = Color.BLUE;
23         private static final Color COLOR_CURR_RANGE  = Color.GREEN;
24         private static final Color COLOR_SELECTED    = Color.RED;
25         private static final Color COLOR_SELECTED_BG = Color.ORANGE;
26         private static final Color COLOR_ALT_SCALE   = Color.RED;
27
28
29         /**
30          * Constructor
31          * @param inTrackInfo Track info object
32          */
33         public ProfileChart(TrackInfo inTrackInfo)
34         {
35                 super(inTrackInfo);
36                 MINIMUM_SIZE = new Dimension(200, 100);
37                 addMouseListener(this);
38         }
39
40
41         /**
42          * Override paint method to draw map
43          * @param g Graphics object
44          */
45         public void paint(Graphics g)
46         {
47                 super.paint(g);
48                 if (_track != null && _track.getNumPoints() > 0)
49                 {
50                         int width = getWidth();
51                         int height = getHeight();
52
53                         // message if no altitudes in track
54                         if (!_track.hasAltitudeData())
55                         {
56                                 g.setColor(COLOR_LINES);
57                                 g.drawString(I18nManager.getText("display.noaltitudes"), 50, height/2);
58                                 return;
59                         }
60
61                         // altitude profile
62                         AltitudeRange altitudeRange = _track.getAltitudeRange();
63                         int minAltitude = altitudeRange.getMinimum();
64                         int maxAltitude = altitudeRange.getMaximum();
65                         int numPoints = _track.getNumPoints();
66                         _xScaleFactor = 1.0 * (width - 2 * BORDER_WIDTH) / numPoints;
67                         double yScaleFactor = 1.0 * (height - 2 * BORDER_WIDTH) / (maxAltitude - minAltitude);
68                         int barWidth = (int) (_xScaleFactor + 1.0);
69                         int selectedPoint = _trackInfo.getSelection().getCurrentPointIndex();
70                         // selection start, end
71                         int selectionStart = -1, selectionEnd = -1;
72                         if (_trackInfo.getSelection().hasRangeSelected()) {
73                                 selectionStart = _trackInfo.getSelection().getStart();
74                                 selectionEnd = _trackInfo.getSelection().getEnd();
75                         }
76
77                         // horizontal lines for scale - set to round numbers eg 500m
78                         int lineScale = getLineScale(minAltitude, maxAltitude);
79                         int altitude = 0;
80                         int y = 0;
81                         if (lineScale > 1)
82                         {
83                                 g.setColor(COLOR_LINES);
84                                 while (altitude < maxAltitude)
85                                 {
86                                         if (altitude > minAltitude)
87                                         {
88                                                 y = height - BORDER_WIDTH - (int) (yScaleFactor * (altitude - minAltitude));
89                                                 g.drawLine(BORDER_WIDTH + 1, y, width - BORDER_WIDTH - 1, y);
90                                         }
91                                         altitude += lineScale;
92                                 }
93                         }
94
95                         try
96                         {
97                                 // loop through points
98                                 Altitude.Format chartFormat = altitudeRange.getFormat();
99                                 for (int p = 0; p < numPoints; p++)
100                                 {
101                                         int x = (int) (_xScaleFactor * p);
102                                         if (p == selectedPoint)
103                                         {
104                                                 g.setColor(COLOR_SELECTED_BG);
105                                                 g.fillRect(BORDER_WIDTH + x, BORDER_WIDTH+1, barWidth, height-2*BORDER_WIDTH-2);
106                                                 g.setColor(COLOR_SELECTED);
107                                         }
108                                         else
109                                         {
110                                                 g.setColor(COLOR_ALT_BARS);
111                                                 if (p >= selectionStart && p <= selectionEnd) {
112                                                         g.setColor(COLOR_CURR_RANGE);
113                                                 }
114                                         }
115                                         if (_track.getPoint(p).getAltitude().isValid())
116                                         {
117                                                 altitude = _track.getPoint(p).getAltitude().getValue(chartFormat);
118                                                 y = (int) (yScaleFactor * (altitude - minAltitude));
119                                                 g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
120                                         }
121                                 }
122                         }
123                         catch (NullPointerException npe) { // ignore, probably due to data being changed
124                         }
125                         // Draw numbers on top of the graph to mark scale
126                         if (lineScale > 1)
127                         {
128                                 int textHeight = g.getFontMetrics().getHeight();
129                                 altitude = 0;
130                                 y = 0;
131                                 g.setColor(COLOR_ALT_SCALE);
132                                 while (altitude < maxAltitude)
133                                 {
134                                         if (altitude > minAltitude)
135                                         {
136                                                 y = height - BORDER_WIDTH - (int) (yScaleFactor * (altitude - minAltitude));
137                                                 // Limit y so String isn't above border
138                                                 if (y < (BORDER_WIDTH + textHeight))
139                                                 {
140                                                         y = BORDER_WIDTH + textHeight;
141                                                 }
142                                                 g.drawString(""+altitude, BORDER_WIDTH + 5, y);
143                                         }
144                                         altitude += lineScale;
145                                 }
146                         }
147                 }
148         }
149
150
151         /**
152          * Work out the scale for the horizontal lines
153          * @param inMin min altitude of data
154          * @param inMax max altitude of data
155          * @return scale separation, or -1 for no scale
156          */
157         private int getLineScale(int inMin, int inMax)
158         {
159                 if ((inMax - inMin) < 50 || inMax < 0)
160                 {
161                         return -1;
162                 }
163                 int numScales = ALTITUDE_SCALES.length;
164                 int scale = 0;
165                 int numLines = 0;
166                 int altitude = 0;
167                 for (int i=0; i<numScales; i++)
168                 {
169                         scale = ALTITUDE_SCALES[i];
170                         if (scale < inMax)
171                         {
172                                 numLines = 0;
173                                 altitude = 0;
174                                 while (altitude < inMax)
175                                 {
176                                         altitude += scale;
177                                         if (altitude > inMin)
178                                         {
179                                                 numLines++;
180                                         }
181                                 }
182                                 if (numLines > 2)
183                                 {
184                                         return scale;
185                                 }
186                         }
187                 }
188                 // no suitable scale found so just use minimum
189                 return ALTITUDE_SCALES[numScales-1];
190         }
191
192
193         /**
194          * Method to inform map that data has changed
195          * @param inTrack track object
196          */
197         public void dataUpdated(Track inTrack)
198         {
199                 _track = inTrack;
200                 repaint();
201         }
202
203
204         /**
205          * React to click on profile display
206          */
207         public void mouseClicked(MouseEvent e)
208         {
209                 // ignore right clicks
210                 if (_track != null && !e.isMetaDown())
211                 {
212                         int xClick = e.getX();
213                         int yClick = e.getY();
214                         // Check click is within main area (not in border)
215                         if (xClick > BORDER_WIDTH && yClick > BORDER_WIDTH && xClick < (getWidth() - BORDER_WIDTH)
216                                 && yClick < (getHeight() - BORDER_WIDTH))
217                         {
218                                 // work out which data point is nearest and select it
219                                 int pointNum = (int) ((e.getX() - BORDER_WIDTH) / _xScaleFactor);
220                                 _trackInfo.selectPoint(pointNum);
221                         }
222                 }
223         }
224 }