]> gitweb.fperrin.net Git - GpsPrune.git/blob - tim/prune/gui/map/MapCanvas.java
Version 5, May 2008
[GpsPrune.git] / tim / prune / gui / map / MapCanvas.java
1 package tim.prune.gui.map;
2
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Graphics;
6 import java.awt.MediaTracker;
7 import java.awt.image.BufferedImage;
8 import java.net.MalformedURLException;
9 import java.net.URL;
10
11 import javax.swing.ImageIcon;
12 import javax.swing.JOptionPane;
13 import javax.swing.JPanel;
14
15 import tim.prune.I18nManager;
16 import tim.prune.data.DoubleRange;
17 import tim.prune.data.Track;
18
19 /**
20  * Class for the map canvas, to display a background map and draw on it
21  */
22 public class MapCanvas extends JPanel
23 {
24
25         private BufferedImage _mapImage = null;
26         private Track _track = null;
27         private DoubleRange _latRange = null, _lonRange = null;
28         private DoubleRange _xRange = null, _yRange = null;
29         private boolean _gettingTiles = false;
30         /** Current zoom level */
31         private int _currZoom = 0;
32         /** Maximum zoom level (to avoid panning) */
33         private int _maxZoom = 0;
34
35
36         /**
37          * Constructor
38          * @param inTrack track object
39          */
40         public MapCanvas(Track inTrack)
41         {
42                 _track = inTrack;
43                 _latRange = inTrack.getLatRange();
44                 _lonRange = inTrack.getLonRange();
45                 _xRange = new DoubleRange(transformX(_lonRange.getMinimum()), transformX(_lonRange.getMaximum()));
46                 _yRange = new DoubleRange(transformY(_latRange.getMinimum()), transformY(_latRange.getMaximum()));
47         }
48
49         /**
50          * Paint method
51          * @see java.awt.Canvas#paint(java.awt.Graphics)
52          */
53         public void paint(Graphics g)
54         {
55                 super.paint(g);
56                 if (_mapImage == null && !_gettingTiles) {
57                         _gettingTiles = true;
58                         new Thread(new Runnable() {
59                                 public void run()
60                                 {
61                                         getMapTiles();
62                                 }
63                         }).start();
64                 }
65                 if (_mapImage != null) {
66                         g.drawImage(_mapImage, 0, 0, 512, 512, null);
67                 }
68         }
69
70         /**
71          * Get the map tiles for the specified track range
72          */
73         private void getMapTiles()
74         {
75                 _mapImage = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
76                 // zoom out until mins and maxes all on same group of four tiles
77                 for (int zoom=15; zoom>1; zoom--)
78                 {
79                         int tx1 = (int) Math.floor(_xRange.getMinimum() * (1<<zoom));
80                         int tx2 = (int) Math.floor(_xRange.getMaximum() * (1<<zoom));
81                         int ty1 = (int) Math.floor(_yRange.getMinimum() * (1<<zoom));
82                         int ty2 = (int) Math.floor(_yRange.getMaximum() * (1<<zoom));
83
84                         // Stop if reached a block of four adjacent tiles
85                         if (tx2 <= (tx1+1) && ty1 >= (ty2-1))
86                         {
87                                 _currZoom = zoom;
88                                 _maxZoom = zoom;
89                                 getMapTiles(tx1, ty1);
90                                 break;
91                         }
92                 }
93                 _gettingTiles = false;
94                 repaint();
95         }
96
97         /**
98          * Get the map tiles for the current zoom level and given tile parameters
99          * @param inTileX x index of leftmost tile
100          * @param inTileY y index of lower tile
101          */
102         private void getMapTiles(int inTileX, int inTileY)
103         {
104                 // Check if tile parameters were given
105                 if (inTileX == -1 || inTileY == -1) {
106                         double tileX = _xRange.getMinimum() * (1<<_currZoom);
107                         double tileY = _yRange.getMinimum() * (1<<_currZoom);
108                         inTileX = (int) Math.floor(tileX);
109                         inTileY = (int) Math.floor(tileY);
110                         // see if should be shifted by 1 to make more central
111                         if (_currZoom != _maxZoom) {
112                                 if ((tileX - inTileX) < 0.5) inTileX--; // don't squash to left
113                                 if ((tileY - inTileY) < 0.5) inTileY--; // don't squash too high
114                         }
115                 }
116                 try
117                 {
118                         ImageIcon[] icons = new ImageIcon[4];
119                         boolean loadingFailed = false;
120                         // Clear map
121                         Graphics g = _mapImage.getGraphics();
122                         g.clearRect(0, 0, 512, 512);
123                         for (int i=0; i<4 && !loadingFailed; i++)
124                         {
125                                 String url = "http://tile.openstreetmap.org/" + _currZoom + "/" + (inTileX + i%2) + "/" + (inTileY + i/2) + ".png";
126                                 icons[i] = new ImageIcon(new URL(url));
127                                 if (icons[i] == null || icons[i].getImage() == null || icons[i].getImageLoadStatus() == MediaTracker.ERRORED)
128                                 {
129                                         loadingFailed = true;
130                                 }
131                                 g.drawImage(icons[i].getImage(), 256*(i%2), 256*(i/2), 256, 256, null);
132                         }
133                         // show message if loading failed
134                         if (loadingFailed) {
135                                 JOptionPane.showMessageDialog(this,
136                                         I18nManager.getText("error.osmimage.failed"),
137                                         I18nManager.getText("error.osmimage.dialogtitle"),
138                                         JOptionPane.ERROR_MESSAGE);
139                         }
140                         // red rectangle
141                         int rectX1 = (int) (256 * ((_xRange.getMinimum() * (1<<_currZoom)) - inTileX));
142                         int rectX2 = (int) (256 * ((_xRange.getMaximum() * (1<<_currZoom)) - inTileX));
143                         int rectY1 = (int) (256 * ((_yRange.getMinimum() * (1<<_currZoom)) - inTileY));
144                         int rectY2 = (int) (256 * ((_yRange.getMaximum() * (1<<_currZoom)) - inTileY));
145                         g.setColor(Color.RED);
146                         g.drawRect(rectX1, rectY1, rectX2-rectX1, rectY2-rectY1);
147                         // draw points
148                         g.setColor(Color.BLUE);
149                         for (int i=0; i<_track.getNumPoints(); i++)
150                         {
151                                 int px = (int) (256 * ((transformX(_track.getPoint(i).getLongitude().getDouble()) * (1<<_currZoom)) - inTileX));
152                                 int py = (int) (256 * ((transformY(_track.getPoint(i).getLatitude().getDouble()) * (1<<_currZoom)) - inTileY));
153                                 g.drawRect(px, py, 2, 2);
154                         }
155                 }
156                 catch (MalformedURLException urle) {
157                         _mapImage = null;
158                 }
159         }
160
161         /**
162          * Zoom out, if not already at minimum zoom
163          */
164         public void zoomOut()
165         {
166                 if (_currZoom >= 2)
167                 {
168                         _currZoom--;
169                         getMapTiles(-1, -1);
170                         repaint();
171                 }
172         }
173
174         /**
175          * Zoom in, if not already at maximum zoom
176          */
177         public void zoomIn()
178         {
179                 if (_currZoom < _maxZoom)
180                 {
181                         _currZoom++;
182                         getMapTiles(-1, -1);
183                         repaint();
184                 }
185         }
186
187         /**
188          * Transform a longitude into an x coordinate
189          * @param inLon longitude in degrees
190          * @return scaled X value from 0 to 1
191          */
192         private static double transformX(double inLon)
193         {
194                 return (inLon + 180.0) / 360.0;
195         }
196
197         /**
198          * Transform a latitude into a y coordinate
199          * @param inLat latitude in degrees
200          * @return scaled Y value from 0 to 1
201          */
202         private static double transformY(double inLat)
203         {
204                 return (1 - Math.log(Math.tan(inLat * Math.PI / 180) + 1 / Math.cos(inLat * Math.PI / 180)) / Math.PI) / 2;
205         }
206
207         /**
208          * @see javax.swing.JComponent#getMinimumSize()
209          */
210         public Dimension getMinimumSize()
211         {
212                 final Dimension minSize = new Dimension(512, 512);
213                 return minSize;
214         }
215
216         /**
217          * @see javax.swing.JComponent#getPreferredSize()
218          */
219         public Dimension getPreferredSize()
220         {
221                 return getMinimumSize();
222         }
223 }