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.config.ColourScheme;
10 import tim.prune.config.Config;
11 import tim.prune.data.Altitude;
12 import tim.prune.data.AltitudeRange;
13 import tim.prune.data.Track;
14 import tim.prune.data.TrackInfo;
17 * Chart component for the profile display
19 public class ProfileChart extends GenericChart
21 /** Current scale factor in x direction*/
22 private double _xScaleFactor = 0.0;
23 /** Possible altitude scales to use */
24 private static final int[] ALTITUDE_SCALES = {10000, 5000, 2000, 1000, 500, 200, 100, 50};
29 * @param inTrackInfo Track info object
31 public ProfileChart(TrackInfo inTrackInfo)
34 MINIMUM_SIZE = new Dimension(200, 100);
35 addMouseListener(this);
40 * Override paint method to draw map
41 * @param g Graphics object
43 public void paint(Graphics g)
46 if (_track != null && _track.getNumPoints() > 0)
48 int width = getWidth();
49 int height = getHeight();
52 final Color barColour = Config.getColourScheme().getColour(ColourScheme.IDX_POINT);
53 final Color rangeColour = Config.getColourScheme().getColour(ColourScheme.IDX_SELECTION);
54 final Color currentColour = Config.getColourScheme().getColour(ColourScheme.IDX_PRIMARY);
55 final Color secondColour = Config.getColourScheme().getColour(ColourScheme.IDX_SECONDARY);
56 final Color lineColour = Config.getColourScheme().getColour(ColourScheme.IDX_LINES);
58 // message if no altitudes in track
59 if (!_track.hasAltitudeData())
61 g.setColor(lineColour);
62 g.drawString(I18nManager.getText("display.noaltitudes"), 50, height/2);
67 AltitudeRange altitudeRange = _track.getAltitudeRange();
68 int minAltitude = altitudeRange.getMinimum();
69 int maxAltitude = altitudeRange.getMaximum();
70 int numPoints = _track.getNumPoints();
71 _xScaleFactor = 1.0 * (width - 2 * BORDER_WIDTH - 1) / numPoints;
72 double yScaleFactor = 1.0 * (height - 2 * BORDER_WIDTH) / (maxAltitude - minAltitude);
73 int barWidth = (int) (_xScaleFactor + 1.0);
74 int selectedPoint = _trackInfo.getSelection().getCurrentPointIndex();
75 // selection start, end
76 int selectionStart = -1, selectionEnd = -1;
77 if (_trackInfo.getSelection().hasRangeSelected()) {
78 selectionStart = _trackInfo.getSelection().getStart();
79 selectionEnd = _trackInfo.getSelection().getEnd();
82 // horizontal lines for scale - set to round numbers eg 500m
83 int lineScale = getLineScale(minAltitude, maxAltitude);
88 g.setColor(lineColour);
89 while (altitude < maxAltitude)
91 if (altitude > minAltitude)
93 y = height - BORDER_WIDTH - (int) (yScaleFactor * (altitude - minAltitude));
94 g.drawLine(BORDER_WIDTH + 1, y, width - BORDER_WIDTH - 1, y);
96 altitude += lineScale;
102 // loop through points
103 Altitude.Format chartFormat = altitudeRange.getFormat();
104 g.setColor(barColour);
105 for (int p = 0; p < numPoints; p++)
107 x = (int) (_xScaleFactor * p) + 1;
108 if (p == selectionStart)
109 g.setColor(rangeColour);
110 else if (p == (selectionEnd+1))
111 g.setColor(barColour);
112 if (_track.getPoint(p).getAltitude().isValid())
114 altitude = _track.getPoint(p).getAltitude().getValue(chartFormat);
115 y = (int) (yScaleFactor * (altitude - minAltitude));
116 g.fillRect(BORDER_WIDTH+x, height-BORDER_WIDTH - y, barWidth, y);
119 // current point (make sure it's drawn last)
120 if (selectedPoint > -1)
122 Altitude alt = _track.getPoint(selectedPoint).getAltitude();
125 x = (int) (_xScaleFactor * selectedPoint) + 1;
126 g.setColor(secondColour);
127 g.fillRect(BORDER_WIDTH + x, BORDER_WIDTH+1, barWidth, height-2*BORDER_WIDTH-2);
128 g.setColor(currentColour);
129 altitude = alt.getValue(chartFormat);
130 y = (int) (yScaleFactor * (altitude - minAltitude));
131 g.fillRect(BORDER_WIDTH + x, height-BORDER_WIDTH - y, barWidth, y);
135 catch (NullPointerException npe) { // ignore, probably due to data being changed
137 // Draw numbers on top of the graph to mark scale
140 int textHeight = g.getFontMetrics().getHeight();
143 g.setColor(currentColour);
144 while (altitude < maxAltitude)
146 if (altitude > minAltitude)
148 y = height - BORDER_WIDTH - (int) (yScaleFactor * (altitude - minAltitude));
149 // Limit y so String isn't above border
150 if (y < (BORDER_WIDTH + textHeight))
152 y = BORDER_WIDTH + textHeight;
154 g.drawString(""+altitude, BORDER_WIDTH + 5, y);
156 altitude += lineScale;
164 * Work out the scale for the horizontal lines
165 * @param inMin min altitude of data
166 * @param inMax max altitude of data
167 * @return scale separation, or -1 for no scale
169 private int getLineScale(int inMin, int inMax)
171 if ((inMax - inMin) < 50 || inMax < 0)
175 int numScales = ALTITUDE_SCALES.length;
179 for (int i=0; i<numScales; i++)
181 scale = ALTITUDE_SCALES[i];
186 while (altitude < inMax)
189 if (altitude > inMin)
200 // no suitable scale found so just use minimum
201 return ALTITUDE_SCALES[numScales-1];
206 * Method to inform map that data has changed
207 * @param inTrack track object
209 public void dataUpdated(Track inTrack)
217 * React to click on profile display
219 public void mouseClicked(MouseEvent e)
221 // ignore right clicks
222 if (_track != null && !e.isMetaDown())
224 int xClick = e.getX();
225 int yClick = e.getY();
226 // Check click is within main area (not in border)
227 if (xClick > BORDER_WIDTH && yClick > BORDER_WIDTH && xClick < (getWidth() - BORDER_WIDTH)
228 && yClick < (getHeight() - BORDER_WIDTH))
230 // work out which data point is nearest and select it
231 int pointNum = (int) ((e.getX() - BORDER_WIDTH) / _xScaleFactor);
232 // If shift clicked, then extend selection
233 if (e.isShiftDown()) {
234 _trackInfo.extendSelection(pointNum);
237 _trackInfo.selectPoint(pointNum);