1 package tim.prune.gui.map;
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;
11 import javax.swing.ImageIcon;
12 import javax.swing.JOptionPane;
13 import javax.swing.JPanel;
15 import tim.prune.I18nManager;
16 import tim.prune.data.DoubleRange;
17 import tim.prune.data.Track;
20 * Class for the map canvas, to display a background map and draw on it
22 public class MapCanvas extends JPanel
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;
38 * @param inTrack track object
40 public MapCanvas(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()));
51 * @see java.awt.Canvas#paint(java.awt.Graphics)
53 public void paint(Graphics g)
56 if (_mapImage == null && !_gettingTiles) {
58 new Thread(new Runnable() {
65 if (_mapImage != null) {
66 g.drawImage(_mapImage, 0, 0, 512, 512, null);
71 * Get the map tiles for the specified track range
73 private void getMapTiles()
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--)
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));
84 // Stop if reached a block of four adjacent tiles
85 if (tx2 <= (tx1+1) && ty1 >= (ty2-1))
89 getMapTiles(tx1, ty1);
93 _gettingTiles = false;
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
102 private void getMapTiles(int inTileX, int inTileY)
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
118 ImageIcon[] icons = new ImageIcon[4];
119 boolean loadingFailed = false;
121 Graphics g = _mapImage.getGraphics();
122 g.clearRect(0, 0, 512, 512);
123 for (int i=0; i<4 && !loadingFailed; i++)
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)
129 loadingFailed = true;
131 g.drawImage(icons[i].getImage(), 256*(i%2), 256*(i/2), 256, 256, null);
133 // show message if loading failed
135 JOptionPane.showMessageDialog(this,
136 I18nManager.getText("error.osmimage.failed"),
137 I18nManager.getText("error.osmimage.dialogtitle"),
138 JOptionPane.ERROR_MESSAGE);
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);
148 g.setColor(Color.BLUE);
149 for (int i=0; i<_track.getNumPoints(); i++)
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);
156 catch (MalformedURLException urle) {
162 * Zoom out, if not already at minimum zoom
164 public void zoomOut()
175 * Zoom in, if not already at maximum zoom
179 if (_currZoom < _maxZoom)
188 * Transform a longitude into an x coordinate
189 * @param inLon longitude in degrees
190 * @return scaled X value from 0 to 1
192 private static double transformX(double inLon)
194 return (inLon + 180.0) / 360.0;
198 * Transform a latitude into a y coordinate
199 * @param inLat latitude in degrees
200 * @return scaled Y value from 0 to 1
202 private static double transformY(double inLat)
204 return (1 - Math.log(Math.tan(inLat * Math.PI / 180) + 1 / Math.cos(inLat * Math.PI / 180)) / Math.PI) / 2;
208 * @see javax.swing.JComponent#getMinimumSize()
210 public Dimension getMinimumSize()
212 final Dimension minSize = new Dimension(512, 512);
217 * @see javax.swing.JComponent#getPreferredSize()
219 public Dimension getPreferredSize()
221 return getMinimumSize();