]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/gui/map/MapCanvas.java
Version 5, May 2008
[GpsPrune.git] / tim / prune / gui / map / MapCanvas.java
diff --git a/tim/prune/gui/map/MapCanvas.java b/tim/prune/gui/map/MapCanvas.java
new file mode 100644 (file)
index 0000000..d306a09
--- /dev/null
@@ -0,0 +1,223 @@
+package tim.prune.gui.map;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.MediaTracker;
+import java.awt.image.BufferedImage;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.ImageIcon;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
+import tim.prune.I18nManager;
+import tim.prune.data.DoubleRange;
+import tim.prune.data.Track;
+
+/**
+ * Class for the map canvas, to display a background map and draw on it
+ */
+public class MapCanvas extends JPanel
+{
+
+       private BufferedImage _mapImage = null;
+       private Track _track = null;
+       private DoubleRange _latRange = null, _lonRange = null;
+       private DoubleRange _xRange = null, _yRange = null;
+       private boolean _gettingTiles = false;
+       /** Current zoom level */
+       private int _currZoom = 0;
+       /** Maximum zoom level (to avoid panning) */
+       private int _maxZoom = 0;
+
+
+       /**
+        * Constructor
+        * @param inTrack track object
+        */
+       public MapCanvas(Track inTrack)
+       {
+               _track = inTrack;
+               _latRange = inTrack.getLatRange();
+               _lonRange = inTrack.getLonRange();
+               _xRange = new DoubleRange(transformX(_lonRange.getMinimum()), transformX(_lonRange.getMaximum()));
+               _yRange = new DoubleRange(transformY(_latRange.getMinimum()), transformY(_latRange.getMaximum()));
+       }
+
+       /**
+        * Paint method
+        * @see java.awt.Canvas#paint(java.awt.Graphics)
+        */
+       public void paint(Graphics g)
+       {
+               super.paint(g);
+               if (_mapImage == null && !_gettingTiles) {
+                       _gettingTiles = true;
+                       new Thread(new Runnable() {
+                               public void run()
+                               {
+                                       getMapTiles();
+                               }
+                       }).start();
+               }
+               if (_mapImage != null) {
+                       g.drawImage(_mapImage, 0, 0, 512, 512, null);
+               }
+       }
+
+       /**
+        * Get the map tiles for the specified track range
+        */
+       private void getMapTiles()
+       {
+               _mapImage = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
+               // zoom out until mins and maxes all on same group of four tiles
+               for (int zoom=15; zoom>1; zoom--)
+               {
+                       int tx1 = (int) Math.floor(_xRange.getMinimum() * (1<<zoom));
+                       int tx2 = (int) Math.floor(_xRange.getMaximum() * (1<<zoom));
+                       int ty1 = (int) Math.floor(_yRange.getMinimum() * (1<<zoom));
+                       int ty2 = (int) Math.floor(_yRange.getMaximum() * (1<<zoom));
+
+                       // Stop if reached a block of four adjacent tiles
+                       if (tx2 <= (tx1+1) && ty1 >= (ty2-1))
+                       {
+                               _currZoom = zoom;
+                               _maxZoom = zoom;
+                               getMapTiles(tx1, ty1);
+                               break;
+                       }
+               }
+               _gettingTiles = false;
+               repaint();
+       }
+
+       /**
+        * Get the map tiles for the current zoom level and given tile parameters
+        * @param inTileX x index of leftmost tile
+        * @param inTileY y index of lower tile
+        */
+       private void getMapTiles(int inTileX, int inTileY)
+       {
+               // Check if tile parameters were given
+               if (inTileX == -1 || inTileY == -1) {
+                       double tileX = _xRange.getMinimum() * (1<<_currZoom);
+                       double tileY = _yRange.getMinimum() * (1<<_currZoom);
+                       inTileX = (int) Math.floor(tileX);
+                       inTileY = (int) Math.floor(tileY);
+                       // see if should be shifted by 1 to make more central
+                       if (_currZoom != _maxZoom) {
+                               if ((tileX - inTileX) < 0.5) inTileX--; // don't squash to left
+                               if ((tileY - inTileY) < 0.5) inTileY--; // don't squash too high
+                       }
+               }
+               try
+               {
+                       ImageIcon[] icons = new ImageIcon[4];
+                       boolean loadingFailed = false;
+                       // Clear map
+                       Graphics g = _mapImage.getGraphics();
+                       g.clearRect(0, 0, 512, 512);
+                       for (int i=0; i<4 && !loadingFailed; i++)
+                       {
+                               String url = "http://tile.openstreetmap.org/" + _currZoom + "/" + (inTileX + i%2) + "/" + (inTileY + i/2) + ".png";
+                               icons[i] = new ImageIcon(new URL(url));
+                               if (icons[i] == null || icons[i].getImage() == null || icons[i].getImageLoadStatus() == MediaTracker.ERRORED)
+                               {
+                                       loadingFailed = true;
+                               }
+                               g.drawImage(icons[i].getImage(), 256*(i%2), 256*(i/2), 256, 256, null);
+                       }
+                       // show message if loading failed
+                       if (loadingFailed) {
+                               JOptionPane.showMessageDialog(this,
+                                       I18nManager.getText("error.osmimage.failed"),
+                                       I18nManager.getText("error.osmimage.dialogtitle"),
+                                       JOptionPane.ERROR_MESSAGE);
+                       }
+                       // red rectangle
+                       int rectX1 = (int) (256 * ((_xRange.getMinimum() * (1<<_currZoom)) - inTileX));
+                       int rectX2 = (int) (256 * ((_xRange.getMaximum() * (1<<_currZoom)) - inTileX));
+                       int rectY1 = (int) (256 * ((_yRange.getMinimum() * (1<<_currZoom)) - inTileY));
+                       int rectY2 = (int) (256 * ((_yRange.getMaximum() * (1<<_currZoom)) - inTileY));
+                       g.setColor(Color.RED);
+                       g.drawRect(rectX1, rectY1, rectX2-rectX1, rectY2-rectY1);
+                       // draw points
+                       g.setColor(Color.BLUE);
+                       for (int i=0; i<_track.getNumPoints(); i++)
+                       {
+                               int px = (int) (256 * ((transformX(_track.getPoint(i).getLongitude().getDouble()) * (1<<_currZoom)) - inTileX));
+                               int py = (int) (256 * ((transformY(_track.getPoint(i).getLatitude().getDouble()) * (1<<_currZoom)) - inTileY));
+                               g.drawRect(px, py, 2, 2);
+                       }
+               }
+               catch (MalformedURLException urle) {
+                       _mapImage = null;
+               }
+       }
+
+       /**
+        * Zoom out, if not already at minimum zoom
+        */
+       public void zoomOut()
+       {
+               if (_currZoom >= 2)
+               {
+                       _currZoom--;
+                       getMapTiles(-1, -1);
+                       repaint();
+               }
+       }
+
+       /**
+        * Zoom in, if not already at maximum zoom
+        */
+       public void zoomIn()
+       {
+               if (_currZoom < _maxZoom)
+               {
+                       _currZoom++;
+                       getMapTiles(-1, -1);
+                       repaint();
+               }
+       }
+
+       /**
+        * Transform a longitude into an x coordinate
+        * @param inLon longitude in degrees
+        * @return scaled X value from 0 to 1
+        */
+       private static double transformX(double inLon)
+       {
+               return (inLon + 180.0) / 360.0;
+       }
+
+       /**
+        * Transform a latitude into a y coordinate
+        * @param inLat latitude in degrees
+        * @return scaled Y value from 0 to 1
+        */
+       private static double transformY(double inLat)
+       {
+               return (1 - Math.log(Math.tan(inLat * Math.PI / 180) + 1 / Math.cos(inLat * Math.PI / 180)) / Math.PI) / 2;
+       }
+
+       /**
+        * @see javax.swing.JComponent#getMinimumSize()
+        */
+       public Dimension getMinimumSize()
+       {
+               final Dimension minSize = new Dimension(512, 512);
+               return minSize;
+       }
+
+       /**
+        * @see javax.swing.JComponent#getPreferredSize()
+        */
+       public Dimension getPreferredSize()
+       {
+               return getMinimumSize();
+       }
+}