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