4 import java.awt.Dimension;
5 import java.awt.Graphics;
6 import java.awt.event.MouseEvent;
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;
15 * Chart component for the profile display
17 public class ProfileChart extends GenericChart
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;
31 * @param inTrackInfo Track info object
33 public ProfileChart(TrackInfo inTrackInfo)
36 MINIMUM_SIZE = new Dimension(200, 100);
37 addMouseListener(this);
42 * Override paint method to draw map
43 * @param g Graphics object
45 public void paint(Graphics g)
48 if (_track != null && _track.getNumPoints() > 0)
50 int width = getWidth();
51 int height = getHeight();
53 // message if no altitudes in track
54 if (!_track.hasAltitudeData())
56 g.setColor(COLOR_LINES);
57 g.drawString(I18nManager.getText("display.noaltitudes"), 50, height/2);
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();
77 // horizontal lines for scale - set to round numbers eg 500m
78 int lineScale = getLineScale(minAltitude, maxAltitude);
83 g.setColor(COLOR_LINES);
84 while (altitude < maxAltitude)
86 if (altitude > minAltitude)
88 y = height - BORDER_WIDTH - (int) (yScaleFactor * (altitude - minAltitude));
89 g.drawLine(BORDER_WIDTH + 1, y, width - BORDER_WIDTH - 1, y);
91 altitude += lineScale;
97 // loop through points
98 Altitude.Format chartFormat = altitudeRange.getFormat();
99 for (int p = 0; p < numPoints; p++)
101 int x = (int) (_xScaleFactor * p);
102 if (p == selectedPoint)
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);
110 g.setColor(COLOR_ALT_BARS);
111 if (p >= selectionStart && p <= selectionEnd) {
112 g.setColor(COLOR_CURR_RANGE);
115 if (_track.getPoint(p).getAltitude().isValid())
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);
123 catch (NullPointerException npe) { // ignore, probably due to data being changed
125 // Draw numbers on top of the graph to mark scale
128 int textHeight = g.getFontMetrics().getHeight();
131 g.setColor(COLOR_ALT_SCALE);
132 while (altitude < maxAltitude)
134 if (altitude > minAltitude)
136 y = height - BORDER_WIDTH - (int) (yScaleFactor * (altitude - minAltitude));
137 // Limit y so String isn't above border
138 if (y < (BORDER_WIDTH + textHeight))
140 y = BORDER_WIDTH + textHeight;
142 g.drawString(""+altitude, BORDER_WIDTH + 5, y);
144 altitude += lineScale;
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
157 private int getLineScale(int inMin, int inMax)
159 if ((inMax - inMin) < 50 || inMax < 0)
163 int numScales = ALTITUDE_SCALES.length;
167 for (int i=0; i<numScales; i++)
169 scale = ALTITUDE_SCALES[i];
174 while (altitude < inMax)
177 if (altitude > inMin)
188 // no suitable scale found so just use minimum
189 return ALTITUDE_SCALES[numScales-1];
194 * Method to inform map that data has changed
195 * @param inTrack track object
197 public void dataUpdated(Track inTrack)
205 * React to click on profile display
207 public void mouseClicked(MouseEvent e)
209 // ignore right clicks
210 if (_track != null && !e.isMetaDown())
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))
218 // work out which data point is nearest and select it
219 int pointNum = (int) ((e.getX() - BORDER_WIDTH) / _xScaleFactor);
220 _trackInfo.selectPoint(pointNum);