altosuilib: Add distance measuring line to site map.
[fw/altos] / altosuilib / AltosSiteMapTile.java
1 /*
2  * Copyright © 2010 Anthony Towns <aj@erisian.com.au>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 package org.altusmetrum.altosuilib_2;
19
20 import java.awt.*;
21 import java.awt.image.*;
22 import javax.swing.*;
23 import javax.imageio.*;
24 import java.awt.geom.*;
25 import java.io.*;
26 import java.util.*;
27 import java.awt.RenderingHints.*;
28 import org.altusmetrum.altoslib_4.*;
29
30 class AltosPoint {
31         Point2D.Double  pt;
32         int             state;
33
34         AltosPoint(Point2D.Double pt, int state) {
35                 this.pt = pt;
36                 this.state = state;
37         }
38 }
39
40 public class AltosSiteMapTile extends JComponent {
41         int px_size;
42         File file;
43         int status;
44
45         Point2D.Double  boost;
46         Point2D.Double  landed;
47         Line2D.Double   line;
48         double          line_course;
49         double          line_dist;
50
51         LinkedList<AltosPoint>  points;
52
53         public synchronized void queue_repaint() {
54                 SwingUtilities.invokeLater(new Runnable() {
55                                 public void run() {
56                                         repaint();
57                                 }
58                         });
59         }
60
61         public void load_map(File pngFile) {
62                 file = pngFile;
63                 queue_repaint();
64         }
65
66         private Font    font = null;
67
68         public void set_font(Font font) {
69                 this.font = font;
70                 this.status = AltosSiteMapCache.success;
71                 queue_repaint();
72         }
73
74         public void set_status(int status) {
75                 file = null;
76                 this.status = status;
77                 queue_repaint();
78         }
79
80         public void clearMap() {
81                 boost = null;
82                 landed = null;
83                 points = null;
84                 file = null;
85                 status = AltosSiteMapCache.success;
86                 queue_repaint();
87                 line = null;
88         }
89
90         static Color stateColors[] = {
91                 Color.WHITE,  // startup
92                 Color.WHITE,  // idle
93                 Color.WHITE,  // pad
94                 Color.RED,    // boost
95                 Color.PINK,   // fast
96                 Color.YELLOW, // coast
97                 Color.CYAN,   // drogue
98                 Color.BLUE,   // main
99                 Color.BLACK   // landed
100         };
101
102         private void draw_circle(Graphics g, Point2D.Double pt) {
103                 g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
104                 g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
105                 g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
106         }
107
108         public void set_boost(Point2D.Double boost) {
109                 this.boost = boost;
110                 queue_repaint();
111         }
112
113         public void set_line(Line2D.Double line, double distance) {
114                 this.line = line;
115                 line_dist = distance;
116                 queue_repaint();
117         }
118
119         private String line_dist() {
120                 String  format;
121                 double  distance = line_dist;
122
123                 if (AltosConvert.imperial_units) {
124                         distance = AltosConvert.meters_to_feet(distance);
125                         if (distance < 10000) {
126                                 format = "%4.0fft";
127                         } else {
128                                 distance /= 5280;
129                                 if (distance < 10)
130                                         format = "%5.3fmi";
131                                 else if (distance < 100)
132                                         format = "%5.2fmi";
133                                 else if (distance < 1000)
134                                         format = "%5.1fmi";
135                                 else
136                                         format = "%5.0fmi";
137                         }
138                 } else {
139                         if (distance < 10000) {
140                                 format = "%4.0fm";
141                         } else {
142                                 distance /= 1000;
143                                 if (distance < 100)
144                                         format = "%5.2fkm";
145                                 else if (distance < 1000)
146                                         format = "%5.1fkm";
147                                 else
148                                         format = "%5.0fkm";
149                         }
150                 }
151                 return String.format(format, distance);
152         }
153
154         public void paint(Graphics g) {
155                 Graphics2D      g2d = (Graphics2D) g;
156                 AltosPoint      prev = null;
157                 Image           img = null;
158
159                 if (file != null)
160                         img = AltosSiteMapCache.get_image(this, file, px_size, px_size);
161
162                 if (img != null) {
163                         g2d.drawImage(img, 0, 0, null);
164                 } else {
165                         g2d.setColor(Color.GRAY);
166                         g2d.fillRect(0, 0, getWidth(), getHeight());
167                         String  message = null;
168                         switch (status) {
169                         case AltosSiteMapCache.loading:
170                                 message = "Loading...";
171                                 break;
172                         case AltosSiteMapCache.bad_request:
173                                 message = "Internal error";
174                                 break;
175                         case AltosSiteMapCache.failed:
176                                 message = "Network error, check connection";
177                                 break;
178                         case AltosSiteMapCache.forbidden:
179                                 message = "Too many requests, try later";
180                                 break;
181                         }
182                         if (message != null && font != null) {
183                                 g2d.setFont(font);
184                                 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
185                                 Rectangle2D     bounds;
186                                 bounds = font.getStringBounds(message, g2d.getFontRenderContext());
187
188                                 float x = getWidth() / 2.0f;
189                                 float y = getHeight() / 2.0f;
190                                 x = x - (float) bounds.getWidth() / 2.0f;
191                                 y = y + (float) bounds.getHeight() / 2.0f;
192                                 g2d.setColor(Color.BLACK);
193                                 g2d.drawString(message, x, y);
194                         }
195                 }
196
197                 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
198                                    RenderingHints.VALUE_ANTIALIAS_ON);
199                 g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
200
201                 if (points != null) {
202                         for (AltosPoint point : points) {
203                                 if (prev != null) {
204                                         if (0 <= point.state && point.state < stateColors.length)
205                                                 g2d.setColor(stateColors[point.state]);
206                                         g2d.draw(new Line2D.Double(prev.pt, point.pt));
207                                 }
208                                 prev = point;
209                         }
210                 }
211                 if (boost != null) {
212                         g2d.setColor(Color.RED);
213                         draw_circle(g2d, boost);
214                 }
215                 if (landed != null) {
216                         g2d.setColor(Color.BLACK);
217                         draw_circle(g2d, landed);
218                 }
219
220                 if (line != null) {
221                         g2d.setColor(Color.BLUE);
222                         g2d.draw(line);
223
224                         String  message = line_dist();
225                         g2d.setFont(font);
226                         g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
227                         Rectangle2D     bounds;
228                         bounds = font.getStringBounds(message, g2d.getFontRenderContext());
229
230                         float x = (float) line.x1;
231                         float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
232
233                         if (line.x1 < line.x2) {
234                                 x -= (float) bounds.getWidth() + 2.0f;
235                         } else {
236                                 x += 2.0f;
237                         }
238                         g2d.drawString(message, x, y);
239                 }
240         }
241
242         public synchronized void show(int state, Point2D.Double last_pt, Point2D.Double pt)
243         {
244                 if (points == null)
245                         points = new LinkedList<AltosPoint>();
246
247                 points.add(new AltosPoint(pt, state));
248
249                 if (state == AltosLib.ao_flight_boost && boost == null)
250                         boost = pt;
251                 if (state == AltosLib.ao_flight_landed && landed == null)
252                         landed = pt;
253                 queue_repaint();
254         }
255
256         public AltosSiteMapTile(int in_px_size) {
257                 px_size = in_px_size;
258                 setPreferredSize(new Dimension(px_size, px_size));
259         }
260 }