Ignore:
Timestamp:
2008-11-07T15:23:05+01:00 (16 years ago)
Author:
stotz
Message:

Patch by r_x and further small enhancements

Location:
applications/viewer/jmapviewer
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • applications/viewer/jmapviewer

    • Property svn:ignore
      •  

        old new  
         1tiles
        12JMapViewer_Demo.jar
        23JMapViewer_src.jar
  • applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java

    r11104 r11783  
    1111import java.awt.event.ActionListener;
    1212import java.awt.geom.Point2D;
    13 import java.awt.image.BufferedImage;
    1413import java.util.LinkedList;
    1514import java.util.List;
    1615
    17 import javax.imageio.ImageIO;
    1816import javax.swing.ImageIcon;
    1917import javax.swing.JButton;
     
    4038public class JMapViewer extends JPanel implements TileLoaderListener {
    4139
    42         private static final long serialVersionUID = 1L;
    43 
    44         /**
    45          * Vectors for clock-wise tile painting
    46          */
    47         protected static final Point[] move = { new Point(1, 0), new Point(0, 1),
    48                         new Point(-1, 0), new Point(0, -1) };
    49 
    50         public static final int MAX_ZOOM = 22;
    51         public static final int MIN_ZOOM = 0;
    52 
    53         protected TileLoader tileLoader;
    54         protected TileCache tileCache;
    55         protected TileSource tileSource;
    56 
    57         protected List<MapMarker> mapMarkerList;
    58         protected boolean mapMarkersVisible;
    59         protected boolean tileGridVisible;
    60 
    61         /**
    62          * x- and y-position of the center of this map-panel on the world map
    63          * denoted in screen pixel regarding the current zoom level.
    64          */
    65         protected Point center;
    66 
    67         /**
    68          * Current zoom level
    69          */
    70         protected int zoom;
    71 
    72         protected JSlider zoomSlider;
    73         protected JButton zoomInButton;
    74         protected JButton zoomOutButton;
    75 
    76         /**
    77          * Hourglass image that is displayed until a map tile has been loaded
    78          */
    79         protected BufferedImage loadingImage;
    80 
    81         JobDispatcher jobDispatcher;
    82 
    83         /**
    84          * Creates a standard {@link JMapViewer} instance that can be controlled via
    85          * mouse: hold right mouse button for moving, double click left mouse button
    86          * or use mouse wheel for zooming. Loaded tiles are stored the
    87          * {@link MemoryTileCache} and the tile loader uses 4 parallel threads for
    88          * retrieving the tiles.
    89          */
    90         public JMapViewer() {
    91                 this(new MemoryTileCache(), 4);
    92                 new DefaultMapController(this);
    93         }
    94 
    95         public JMapViewer(TileCache tileCache, int downloadThreadCount) {
    96                 super();
    97                 tileSource = new OsmTileSource.Mapnik();
    98                 tileLoader = new OsmTileLoader(this);
    99                 this.tileCache = tileCache;
    100                 jobDispatcher = JobDispatcher.getInstance();
    101                 mapMarkerList = new LinkedList<MapMarker>();
    102                 mapMarkersVisible = true;
    103                 tileGridVisible = false;
    104                 setLayout(null);
    105                 initializeZoomSlider();
    106                 setMinimumSize(new Dimension(Tile.SIZE, Tile.SIZE));
    107                 setPreferredSize(new Dimension(400, 400));
    108                 try {
    109                         loadingImage = ImageIO.read(JMapViewer.class
    110                                         .getResourceAsStream("images/hourglass.png"));
    111                 } catch (Exception e1) {
    112                         loadingImage = null;
    113                 }
    114                 setDisplayPositionByLatLon(50, 9, 3);
    115         }
    116 
    117         protected void initializeZoomSlider() {
    118                 zoomSlider = new JSlider(MIN_ZOOM, tileSource.getMaxZoom());
    119                 zoomSlider.setOrientation(JSlider.VERTICAL);
    120                 zoomSlider.setBounds(10, 10, 30, 150);
    121                 zoomSlider.setOpaque(false);
    122                 zoomSlider.addChangeListener(new ChangeListener() {
    123                         public void stateChanged(ChangeEvent e) {
    124                                 setZoom(zoomSlider.getValue());
    125                         }
    126                 });
    127                 add(zoomSlider);
    128                 int size = 18;
    129                 try {
    130                         ImageIcon icon = new ImageIcon(getClass().getResource(
    131                                         "images/plus.png"));
    132                         zoomInButton = new JButton(icon);
    133                 } catch (Exception e) {
    134                         zoomInButton = new JButton("+");
    135                         zoomInButton.setFont(new Font("sansserif", Font.BOLD, 9));
    136                         zoomInButton.setMargin(new Insets(0, 0, 0, 0));
    137                 }
    138                 zoomInButton.setBounds(4, 155, size, size);
    139                 zoomInButton.addActionListener(new ActionListener() {
    140 
    141                         public void actionPerformed(ActionEvent e) {
    142                                 zoomIn();
    143                         }
    144                 });
    145                 add(zoomInButton);
    146                 try {
    147                         ImageIcon icon = new ImageIcon(getClass().getResource(
    148                                         "images/minus.png"));
    149                         zoomOutButton = new JButton(icon);
    150                 } catch (Exception e) {
    151                         zoomOutButton = new JButton("-");
    152                         zoomOutButton.setFont(new Font("sansserif", Font.BOLD, 9));
    153                         zoomOutButton.setMargin(new Insets(0, 0, 0, 0));
    154                 }
    155                 zoomOutButton.setBounds(8 + size, 155, size, size);
    156                 zoomOutButton.addActionListener(new ActionListener() {
    157 
    158                         public void actionPerformed(ActionEvent e) {
    159                                 zoomOut();
    160                         }
    161                 });
    162                 add(zoomOutButton);
    163         }
    164 
    165         /**
    166          * Changes the map pane so that it is centered on the specified coordinate
    167          * at the given zoom level.
    168          *
    169          * @param lat
    170          *            latitude of the specified coordinate
    171          * @param lon
    172          *            longitude of the specified coordinate
    173          * @param zoom
    174          *            {@link #MIN_ZOOM} <= zoom level <= {@link #MAX_ZOOM}
    175          */
    176         public void setDisplayPositionByLatLon(double lat, double lon, int zoom) {
    177                 setDisplayPositionByLatLon(new Point(getWidth() / 2, getHeight() / 2),
    178                                 lat, lon, zoom);
    179         }
    180 
    181         /**
    182          * Changes the map pane so that the specified coordinate at the given zoom
    183          * level is displayed on the map at the screen coordinate
    184          * <code>mapPoint</code>.
    185          *
    186          * @param mapPoint
    187          *            point on the map denoted in pixels where the coordinate should
    188          *            be set
    189          * @param lat
    190          *            latitude of the specified coordinate
    191          * @param lon
    192          *            longitude of the specified coordinate
    193          * @param zoom
    194          *            {@link #MIN_ZOOM} <= zoom level <=
    195          *            {@link TileSource#getMaxZoom()}
    196          */
    197         public void setDisplayPositionByLatLon(Point mapPoint, double lat,
    198                         double lon, int zoom) {
    199                 int x = OsmMercator.LonToX(lon, zoom);
    200                 int y = OsmMercator.LatToY(lat, zoom);
    201                 setDisplayPosition(mapPoint, x, y, zoom);
    202         }
    203 
    204         public void setDisplayPosition(int x, int y, int zoom) {
    205                 setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), x, y,
    206                                 zoom);
    207         }
    208 
    209         public void setDisplayPosition(Point mapPoint, int x, int y, int zoom) {
    210                 if (zoom > tileSource.getMaxZoom() || zoom < MIN_ZOOM)
    211                         return;
    212 
    213                 // Get the plain tile number
    214                 Point p = new Point();
    215                 p.x = x - mapPoint.x + getWidth() / 2;
    216                 p.y = y - mapPoint.y + getHeight() / 2;
    217                 center = p;
    218                 setIgnoreRepaint(true);
    219                 try {
    220                         int oldZoom = this.zoom;
    221                         this.zoom = zoom;
    222                         if (oldZoom != zoom)
    223                                 zoomChanged(oldZoom);
    224                         if (zoomSlider.getValue() != zoom)
    225                                 zoomSlider.setValue(zoom);
    226                 } finally {
    227                         setIgnoreRepaint(false);
    228                         repaint();
    229                 }
    230         }
    231 
    232         /**
    233          * Sets the displayed map pane and zoom level so that all map markers are
    234          * visible.
    235          */
    236         public void setDisplayToFitMapMarkers() {
    237                 if (mapMarkerList == null || mapMarkerList.size() == 0)
    238                         return;
    239                 int x_min = Integer.MAX_VALUE;
    240                 int y_min = Integer.MAX_VALUE;
    241                 int x_max = Integer.MIN_VALUE;
    242                 int y_max = Integer.MIN_VALUE;
    243                 int mapZoomMax = tileSource.getMaxZoom();
    244                 for (MapMarker marker : mapMarkerList) {
    245                         int x = OsmMercator.LonToX(marker.getLon(), mapZoomMax);
    246                         int y = OsmMercator.LatToY(marker.getLat(), mapZoomMax);
    247                         x_max = Math.max(x_max, x);
    248                         y_max = Math.max(y_max, y);
    249                         x_min = Math.min(x_min, x);
    250                         y_min = Math.min(y_min, y);
    251                 }
    252                 int height = Math.max(0, getHeight());
    253                 int width = Math.max(0, getWidth());
    254                 // System.out.println(x_min + " < x < " + x_max);
    255                 // System.out.println(y_min + " < y < " + y_max);
    256                 // System.out.println("tiles: " + width + " " + height);
    257                 int newZoom = mapZoomMax;
    258                 int x = x_max - x_min;
    259                 int y = y_max - y_min;
    260                 while (x > width || y > height) {
    261                         // System.out.println("zoom: " + zoom + " -> " + x + " " + y);
    262                         newZoom--;
    263                         x >>= 1;
    264                         y >>= 1;
    265                 }
    266                 x = x_min + (x_max - x_min) / 2;
    267                 y = y_min + (y_max - y_min) / 2;
    268                 int z = 1 << (mapZoomMax - newZoom);
    269                 x /= z;
    270                 y /= z;
    271                 setDisplayPosition(x, y, newZoom);
    272         }
    273 
    274         public Point2D.Double getPosition() {
    275                 double lon = OsmMercator.XToLon(center.x, zoom);
    276                 double lat = OsmMercator.YToLat(center.y, zoom);
    277                 return new Point2D.Double(lat, lon);
    278         }
    279 
    280         public Point2D.Double getPosition(Point mapPoint) {
    281                 int x = center.x + mapPoint.x - getWidth() / 2;
    282                 int y = center.y + mapPoint.y - getHeight() / 2;
    283                 double lon = OsmMercator.XToLon(x, zoom);
    284                 double lat = OsmMercator.YToLat(y, zoom);
    285                 return new Point2D.Double(lat, lon);
    286         }
    287 
    288         /**
    289          * Calculates the position on the map of a given coordinate
    290          *
    291          * @param lat
    292          * @param lon
    293          * @return point on the map or <code>null</code> if the point is not visible
    294          */
    295         public Point getMapPosition(double lat, double lon) {
    296                 int x = OsmMercator.LonToX(lon, zoom);
    297                 int y = OsmMercator.LatToY(lat, zoom);
    298                 x -= center.x - getWidth() / 2;
    299                 y -= center.y - getHeight() / 2;
    300                 if (x < 0 || y < 0 || x > getWidth() || y > getHeight())
    301                         return null;
    302                 return new Point(x, y);
    303         }
    304 
    305         @Override
    306         protected void paintComponent(Graphics g) {
    307                 super.paintComponent(g);
    308 
    309                 int iMove = 0;
    310 
    311                 int tilex = center.x / Tile.SIZE;
    312                 int tiley = center.y / Tile.SIZE;
    313                 int off_x = (center.x % Tile.SIZE);
    314                 int off_y = (center.y % Tile.SIZE);
    315 
    316                 int w2 = getWidth() / 2;
    317                 int h2 = getHeight() / 2;
    318                 int posx = w2 - off_x;
    319                 int posy = h2 - off_y;
    320 
    321                 int diff_left = off_x;
    322                 int diff_right = Tile.SIZE - off_x;
    323                 int diff_top = off_y;
    324                 int diff_bottom = Tile.SIZE - off_y;
    325 
    326                 boolean start_left = diff_left < diff_right;
    327                 boolean start_top = diff_top < diff_bottom;
    328 
    329                 if (start_top) {
    330                         if (start_left)
    331                                 iMove = 2;
    332                         else
    333                                 iMove = 3;
    334                 } else {
    335                         if (start_left)
    336                                 iMove = 1;
    337                         else
    338                                 iMove = 0;
    339                 } // calculate the visibility borders
    340                 int x_min = -Tile.SIZE;
    341                 int y_min = -Tile.SIZE;
    342                 int x_max = getWidth();
    343                 int y_max = getHeight();
    344 
    345                 // paint the tiles in a spiral, starting from center of the map
    346                 boolean painted = true;
    347                 int x = 0;
    348                 while (painted) {
    349                         painted = false;
    350                         for (int i = 0; i < 4; i++) {
    351                                 if (i % 2 == 0)
    352                                         x++;
    353                                 for (int j = 0; j < x; j++) {
    354                                         if (x_min <= posx && posx <= x_max && y_min <= posy
    355                                                         && posy <= y_max) {
    356                                                 // tile is visible
    357                                                 Tile tile = getTile(tilex, tiley, zoom);
    358                                                 if (tile != null) {
    359                                                         painted = true;
    360                                                         tile.paint(g, posx, posy);
    361                                                         if (tileGridVisible)
    362                                                                 g.drawRect(posx, posy, Tile.SIZE, Tile.SIZE);
    363                                                 }
    364                                         }
    365                                         Point p = move[iMove];
    366                                         posx += p.x * Tile.SIZE;
    367                                         posy += p.y * Tile.SIZE;
    368                                         tilex += p.x;
    369                                         tiley += p.y;
    370                                 }
    371                                 iMove = (iMove + 1) % move.length;
    372                         }
    373                 }
    374                 // outer border of the map
    375                 int mapSize = Tile.SIZE << zoom;
    376                 g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
    377 
    378                 // g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
    379                 if (!mapMarkersVisible || mapMarkerList == null)
    380                         return;
    381                 for (MapMarker marker : mapMarkerList) {
    382                         Point p = getMapPosition(marker.getLat(), marker.getLon());
    383                         // System.out.println(marker + " -> " + p);
    384                         if (p != null)
    385                                 marker.paint(g, p);
    386                 }
    387         }
    388 
    389         /**
    390          * Moves the visible map pane.
    391          *
    392          * @param x
    393          *            horizontal movement in pixel.
    394          * @param y
    395          *            vertical movement in pixel
    396          */
    397         public void moveMap(int x, int y) {
    398                 center.x += x;
    399                 center.y += y;
    400                 repaint();
    401         }
    402 
    403         /**
    404          * @return the current zoom level
    405          */
    406         public int getZoom() {
    407                 return zoom;
    408         }
    409 
    410         /**
    411          * Increases the current zoom level by one
    412          */
    413         public void zoomIn() {
    414                 setZoom(zoom + 1);
    415         }
    416 
    417         /**
    418          * Increases the current zoom level by one
    419          */
    420         public void zoomIn(Point mapPoint) {
    421                 setZoom(zoom + 1, mapPoint);
    422         }
    423 
    424         /**
    425          * Decreases the current zoom level by one
    426          */
    427         public void zoomOut() {
    428                 setZoom(zoom - 1);
    429         }
    430 
    431         /**
    432          * Decreases the current zoom level by one
    433          */
    434         public void zoomOut(Point mapPoint) {
    435                 setZoom(zoom - 1, mapPoint);
    436         }
    437 
    438         public void setZoom(int zoom, Point mapPoint) {
    439                 if (zoom > tileSource.getMaxZoom() || zoom == this.zoom)
    440                         return;
    441                 Point2D.Double zoomPos = getPosition(mapPoint);
    442                 jobDispatcher.cancelOutstandingJobs(); // Clearing outstanding load
    443                 // requests
    444                 setDisplayPositionByLatLon(mapPoint, zoomPos.x, zoomPos.y, zoom);
    445         }
    446 
    447         public void setZoom(int zoom) {
    448                 setZoom(zoom, new Point(getWidth() / 2, getHeight() / 2));
    449         }
    450 
    451         /**
    452          * retrieves a tile from the cache. If the tile is not present in the cache
    453          * a load job is added to the working queue of {@link JobThread}.
    454          *
    455          * @param tilex
    456          * @param tiley
    457          * @param zoom
    458          * @return specified tile from the cache or <code>null</code> if the tile
    459          *         was not found in the cache.
    460          */
    461         protected Tile getTile(int tilex, int tiley, int zoom) {
    462                 int max = (1 << zoom);
    463                 if (tilex < 0 || tilex >= max || tiley < 0 || tiley >= max)
    464                         return null;
    465                 Tile tile = tileCache.getTile(tileSource, tilex, tiley, zoom);
    466                 if (tile == null) {
    467                         tile = new Tile(tileSource, tilex, tiley, zoom, loadingImage);
    468                         tileCache.addTile(tile);
    469                         tile.loadPlaceholderFromCache(tileCache);
    470                 }
    471                 if (!tile.isLoaded()) {
    472                         jobDispatcher.addJob(tileLoader.createTileLoaderJob(tileSource,
    473                                         tilex, tiley, zoom));
    474                 }
    475                 return tile;
    476         }
    477 
    478         /**
    479          * Every time the zoom level changes this method is called. Override it in
    480          * derived implementations for adapting zoom dependent values. The new zoom
    481          * level can be obtained via {@link #getZoom()}.
    482          *
    483          * @param oldZoom
    484          *            the previous zoom level
    485          */
    486         protected void zoomChanged(int oldZoom) {
    487                 zoomSlider.setToolTipText("Zoom level " + zoom);
    488                 zoomInButton.setToolTipText("Zoom to level " + (zoom + 1));
    489                 zoomOutButton.setToolTipText("Zoom to level " + (zoom - 1));
    490                 zoomOutButton.setEnabled(zoom > 0);
    491                 zoomInButton.setEnabled(zoom < tileSource.getMaxZoom());
    492         }
    493 
    494         public boolean isTileGridVisible() {
    495                 return tileGridVisible;
    496         }
    497 
    498         public void setTileGridVisible(boolean tileGridVisible) {
    499                 this.tileGridVisible = tileGridVisible;
    500                 repaint();
    501         }
    502 
    503         public boolean getMapMarkersVisible() {
    504                 return mapMarkersVisible;
    505         }
    506 
    507         /**
    508          * Enables or disables painting of the {@link MapMarker}
    509          *
    510          * @param mapMarkersVisible
    511          * @see #addMapMarker(MapMarker)
    512          * @see #getMapMarkerList()
    513          */
    514         public void setMapMarkerVisible(boolean mapMarkersVisible) {
    515                 this.mapMarkersVisible = mapMarkersVisible;
    516                 repaint();
    517         }
    518 
    519         public void setMapMarkerList(List<MapMarker> mapMarkerList) {
    520                 this.mapMarkerList = mapMarkerList;
    521                 repaint();
    522         }
    523 
    524         public List<MapMarker> getMapMarkerList() {
    525                 return mapMarkerList;
    526         }
    527 
    528         public void addMapMarker(MapMarker marker) {
    529                 mapMarkerList.add(marker);
    530         }
    531 
    532         public void setZoomContolsVisible(boolean visible) {
    533                 zoomSlider.setVisible(visible);
    534                 zoomInButton.setVisible(visible);
    535                 zoomOutButton.setVisible(visible);
    536         }
    537 
    538         public boolean getZoomContolsVisible() {
    539                 return zoomSlider.isVisible();
    540         }
    541 
    542         public TileCache getTileCache() {
    543                 return tileCache;
    544         }
    545 
    546         public TileLoader getTileLoader() {
    547                 return tileLoader;
    548         }
    549 
    550         public void setTileLoader(TileLoader tileLoader) {
    551                 this.tileLoader = tileLoader;
    552         }
    553 
    554         public TileSource getTileLayerSource() {
    555                 return tileSource;
    556         }
    557 
    558         public void setTileSource(TileSource tileSource) {
    559                 if (tileSource.getMaxZoom() > MAX_ZOOM)
    560                         throw new RuntimeException("Zoom level too high");
    561                 this.tileSource = tileSource;
    562                 zoomSlider.setMaximum(tileSource.getMaxZoom());
    563                 jobDispatcher.cancelOutstandingJobs();
    564                 if (zoom > tileSource.getMaxZoom())
    565                         setZoom(tileSource.getMaxZoom());
    566                 repaint();
    567         }
    568 
    569         public void tileLoadingFinished(Tile tile) {
    570                 repaint();
    571         }
     40    private static final long serialVersionUID = 1L;
     41
     42    /**
     43     * Vectors for clock-wise tile painting
     44     */
     45    protected static final Point[] move = { new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1) };
     46
     47    public static final int MAX_ZOOM = 22;
     48    public static final int MIN_ZOOM = 0;
     49
     50    protected TileLoader tileLoader;
     51    protected TileCache tileCache;
     52    protected TileSource tileSource;
     53
     54    protected List<MapMarker> mapMarkerList;
     55    protected boolean mapMarkersVisible;
     56    protected boolean tileGridVisible;
     57
     58    /**
     59     * x- and y-position of the center of this map-panel on the world map
     60     * denoted in screen pixel regarding the current zoom level.
     61     */
     62    protected Point center;
     63
     64    /**
     65     * Current zoom level
     66     */
     67    protected int zoom;
     68
     69    protected JSlider zoomSlider;
     70    protected JButton zoomInButton;
     71    protected JButton zoomOutButton;
     72
     73    JobDispatcher jobDispatcher;
     74
     75    /**
     76     * Creates a standard {@link JMapViewer} instance that can be controlled via
     77     * mouse: hold right mouse button for moving, double click left mouse button
     78     * or use mouse wheel for zooming. Loaded tiles are stored the
     79     * {@link MemoryTileCache} and the tile loader uses 4 parallel threads for
     80     * retrieving the tiles.
     81     */
     82    public JMapViewer() {
     83        this(new MemoryTileCache(), 4);
     84        new DefaultMapController(this);
     85    }
     86
     87    public JMapViewer(TileCache tileCache, int downloadThreadCount) {
     88        super();
     89        tileSource = new OsmTileSource.Mapnik();
     90        tileLoader = new OsmTileLoader(this);
     91        this.tileCache = tileCache;
     92        jobDispatcher = JobDispatcher.getInstance();
     93        mapMarkerList = new LinkedList<MapMarker>();
     94        mapMarkersVisible = true;
     95        tileGridVisible = false;
     96        setLayout(null);
     97        initializeZoomSlider();
     98        setMinimumSize(new Dimension(Tile.SIZE, Tile.SIZE));
     99        setPreferredSize(new Dimension(400, 400));
     100        setDisplayPositionByLatLon(50, 9, 3);
     101    }
     102
     103    protected void initializeZoomSlider() {
     104        zoomSlider = new JSlider(MIN_ZOOM, tileSource.getMaxZoom());
     105        zoomSlider.setOrientation(JSlider.VERTICAL);
     106        zoomSlider.setBounds(10, 10, 30, 150);
     107        zoomSlider.setOpaque(false);
     108        zoomSlider.addChangeListener(new ChangeListener() {
     109            public void stateChanged(ChangeEvent e) {
     110                setZoom(zoomSlider.getValue());
     111            }
     112        });
     113        add(zoomSlider);
     114        int size = 18;
     115        try {
     116            ImageIcon icon = new ImageIcon(getClass().getResource("images/plus.png"));
     117            zoomInButton = new JButton(icon);
     118        } catch (Exception e) {
     119            zoomInButton = new JButton("+");
     120            zoomInButton.setFont(new Font("sansserif", Font.BOLD, 9));
     121            zoomInButton.setMargin(new Insets(0, 0, 0, 0));
     122        }
     123        zoomInButton.setBounds(4, 155, size, size);
     124        zoomInButton.addActionListener(new ActionListener() {
     125
     126            public void actionPerformed(ActionEvent e) {
     127                zoomIn();
     128            }
     129        });
     130        add(zoomInButton);
     131        try {
     132            ImageIcon icon = new ImageIcon(getClass().getResource("images/minus.png"));
     133            zoomOutButton = new JButton(icon);
     134        } catch (Exception e) {
     135            zoomOutButton = new JButton("-");
     136            zoomOutButton.setFont(new Font("sansserif", Font.BOLD, 9));
     137            zoomOutButton.setMargin(new Insets(0, 0, 0, 0));
     138        }
     139        zoomOutButton.setBounds(8 + size, 155, size, size);
     140        zoomOutButton.addActionListener(new ActionListener() {
     141
     142            public void actionPerformed(ActionEvent e) {
     143                zoomOut();
     144            }
     145        });
     146        add(zoomOutButton);
     147    }
     148
     149    /**
     150     * Changes the map pane so that it is centered on the specified coordinate
     151     * at the given zoom level.
     152     *
     153     * @param lat
     154     *            latitude of the specified coordinate
     155     * @param lon
     156     *            longitude of the specified coordinate
     157     * @param zoom
     158     *            {@link #MIN_ZOOM} <= zoom level <= {@link #MAX_ZOOM}
     159     */
     160    public void setDisplayPositionByLatLon(double lat, double lon, int zoom) {
     161        setDisplayPositionByLatLon(new Point(getWidth() / 2, getHeight() / 2), lat, lon, zoom);
     162    }
     163
     164    /**
     165     * Changes the map pane so that the specified coordinate at the given zoom
     166     * level is displayed on the map at the screen coordinate
     167     * <code>mapPoint</code>.
     168     *
     169     * @param mapPoint
     170     *            point on the map denoted in pixels where the coordinate should
     171     *            be set
     172     * @param lat
     173     *            latitude of the specified coordinate
     174     * @param lon
     175     *            longitude of the specified coordinate
     176     * @param zoom
     177     *            {@link #MIN_ZOOM} <= zoom level <=
     178     *            {@link TileSource#getMaxZoom()}
     179     */
     180    public void setDisplayPositionByLatLon(Point mapPoint, double lat, double lon, int zoom) {
     181        int x = OsmMercator.LonToX(lon, zoom);
     182        int y = OsmMercator.LatToY(lat, zoom);
     183        setDisplayPosition(mapPoint, x, y, zoom);
     184    }
     185
     186    public void setDisplayPosition(int x, int y, int zoom) {
     187        setDisplayPosition(new Point(getWidth() / 2, getHeight() / 2), x, y, zoom);
     188    }
     189
     190    public void setDisplayPosition(Point mapPoint, int x, int y, int zoom) {
     191        if (zoom > tileSource.getMaxZoom() || zoom < MIN_ZOOM)
     192            return;
     193
     194        // Get the plain tile number
     195        Point p = new Point();
     196        p.x = x - mapPoint.x + getWidth() / 2;
     197        p.y = y - mapPoint.y + getHeight() / 2;
     198        center = p;
     199        setIgnoreRepaint(true);
     200        try {
     201            int oldZoom = this.zoom;
     202            this.zoom = zoom;
     203            if (oldZoom != zoom)
     204                zoomChanged(oldZoom);
     205            if (zoomSlider.getValue() != zoom)
     206                zoomSlider.setValue(zoom);
     207        } finally {
     208            setIgnoreRepaint(false);
     209            repaint();
     210        }
     211    }
     212
     213    /**
     214     * Sets the displayed map pane and zoom level so that all map markers are
     215     * visible.
     216     */
     217    public void setDisplayToFitMapMarkers() {
     218        if (mapMarkerList == null || mapMarkerList.size() == 0)
     219            return;
     220        int x_min = Integer.MAX_VALUE;
     221        int y_min = Integer.MAX_VALUE;
     222        int x_max = Integer.MIN_VALUE;
     223        int y_max = Integer.MIN_VALUE;
     224        int mapZoomMax = tileSource.getMaxZoom();
     225        for (MapMarker marker : mapMarkerList) {
     226            int x = OsmMercator.LonToX(marker.getLon(), mapZoomMax);
     227            int y = OsmMercator.LatToY(marker.getLat(), mapZoomMax);
     228            x_max = Math.max(x_max, x);
     229            y_max = Math.max(y_max, y);
     230            x_min = Math.min(x_min, x);
     231            y_min = Math.min(y_min, y);
     232        }
     233        int height = Math.max(0, getHeight());
     234        int width = Math.max(0, getWidth());
     235        // System.out.println(x_min + " < x < " + x_max);
     236        // System.out.println(y_min + " < y < " + y_max);
     237        // System.out.println("tiles: " + width + " " + height);
     238        int newZoom = mapZoomMax;
     239        int x = x_max - x_min;
     240        int y = y_max - y_min;
     241        while (x > width || y > height) {
     242            // System.out.println("zoom: " + zoom + " -> " + x + " " + y);
     243            newZoom--;
     244            x >>= 1;
     245            y >>= 1;
     246        }
     247        x = x_min + (x_max - x_min) / 2;
     248        y = y_min + (y_max - y_min) / 2;
     249        int z = 1 << (mapZoomMax - newZoom);
     250        x /= z;
     251        y /= z;
     252        setDisplayPosition(x, y, newZoom);
     253    }
     254
     255    public Point2D.Double getPosition() {
     256        double lon = OsmMercator.XToLon(center.x, zoom);
     257        double lat = OsmMercator.YToLat(center.y, zoom);
     258        return new Point2D.Double(lat, lon);
     259    }
     260
     261    public Point2D.Double getPosition(Point mapPoint) {
     262        int x = center.x + mapPoint.x - getWidth() / 2;
     263        int y = center.y + mapPoint.y - getHeight() / 2;
     264        double lon = OsmMercator.XToLon(x, zoom);
     265        double lat = OsmMercator.YToLat(y, zoom);
     266        return new Point2D.Double(lat, lon);
     267    }
     268
     269    /**
     270     * Calculates the position on the map of a given coordinate
     271     *
     272     * @param lat
     273     * @param lon
     274     * @return point on the map or <code>null</code> if the point is not visible
     275     */
     276    public Point getMapPosition(double lat, double lon) {
     277        int x = OsmMercator.LonToX(lon, zoom);
     278        int y = OsmMercator.LatToY(lat, zoom);
     279        x -= center.x - getWidth() / 2;
     280        y -= center.y - getHeight() / 2;
     281        if (x < 0 || y < 0 || x > getWidth() || y > getHeight())
     282            return null;
     283        return new Point(x, y);
     284    }
     285
     286    @Override
     287    protected void paintComponent(Graphics g) {
     288        super.paintComponent(g);
     289
     290        int iMove = 0;
     291
     292        int tilex = center.x / Tile.SIZE;
     293        int tiley = center.y / Tile.SIZE;
     294        int off_x = (center.x % Tile.SIZE);
     295        int off_y = (center.y % Tile.SIZE);
     296
     297        int w2 = getWidth() / 2;
     298        int h2 = getHeight() / 2;
     299        int posx = w2 - off_x;
     300        int posy = h2 - off_y;
     301
     302        int diff_left = off_x;
     303        int diff_right = Tile.SIZE - off_x;
     304        int diff_top = off_y;
     305        int diff_bottom = Tile.SIZE - off_y;
     306
     307        boolean start_left = diff_left < diff_right;
     308        boolean start_top = diff_top < diff_bottom;
     309
     310        if (start_top) {
     311            if (start_left)
     312                iMove = 2;
     313            else
     314                iMove = 3;
     315        } else {
     316            if (start_left)
     317                iMove = 1;
     318            else
     319                iMove = 0;
     320        } // calculate the visibility borders
     321        int x_min = -Tile.SIZE;
     322        int y_min = -Tile.SIZE;
     323        int x_max = getWidth();
     324        int y_max = getHeight();
     325
     326        // paint the tiles in a spiral, starting from center of the map
     327        boolean painted = true;
     328        int x = 0;
     329        while (painted) {
     330            painted = false;
     331            for (int i = 0; i < 4; i++) {
     332                if (i % 2 == 0)
     333                    x++;
     334                for (int j = 0; j < x; j++) {
     335                    if (x_min <= posx && posx <= x_max && y_min <= posy && posy <= y_max) {
     336                        // tile is visible
     337                        Tile tile = getTile(tilex, tiley, zoom);
     338                        if (tile != null) {
     339                            painted = true;
     340                            tile.paint(g, posx, posy);
     341                            if (tileGridVisible)
     342                                g.drawRect(posx, posy, Tile.SIZE, Tile.SIZE);
     343                        }
     344                    }
     345                    Point p = move[iMove];
     346                    posx += p.x * Tile.SIZE;
     347                    posy += p.y * Tile.SIZE;
     348                    tilex += p.x;
     349                    tiley += p.y;
     350                }
     351                iMove = (iMove + 1) % move.length;
     352            }
     353        }
     354        // outer border of the map
     355        int mapSize = Tile.SIZE << zoom;
     356        g.drawRect(w2 - center.x, h2 - center.y, mapSize, mapSize);
     357
     358        // g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20);
     359        if (!mapMarkersVisible || mapMarkerList == null)
     360            return;
     361        for (MapMarker marker : mapMarkerList) {
     362            Point p = getMapPosition(marker.getLat(), marker.getLon());
     363            // System.out.println(marker + " -> " + p);
     364            if (p != null)
     365                marker.paint(g, p);
     366        }
     367    }
     368
     369    /**
     370     * Moves the visible map pane.
     371     *
     372     * @param x
     373     *            horizontal movement in pixel.
     374     * @param y
     375     *            vertical movement in pixel
     376     */
     377    public void moveMap(int x, int y) {
     378        center.x += x;
     379        center.y += y;
     380        repaint();
     381    }
     382
     383    /**
     384     * @return the current zoom level
     385     */
     386    public int getZoom() {
     387        return zoom;
     388    }
     389
     390    /**
     391     * Increases the current zoom level by one
     392     */
     393    public void zoomIn() {
     394        setZoom(zoom + 1);
     395    }
     396
     397    /**
     398     * Increases the current zoom level by one
     399     */
     400    public void zoomIn(Point mapPoint) {
     401        setZoom(zoom + 1, mapPoint);
     402    }
     403
     404    /**
     405     * Decreases the current zoom level by one
     406     */
     407    public void zoomOut() {
     408        setZoom(zoom - 1);
     409    }
     410
     411    /**
     412     * Decreases the current zoom level by one
     413     */
     414    public void zoomOut(Point mapPoint) {
     415        setZoom(zoom - 1, mapPoint);
     416    }
     417
     418    public void setZoom(int zoom, Point mapPoint) {
     419        if (zoom > tileSource.getMaxZoom() || zoom < tileSource.getMinZoom() || zoom == this.zoom)
     420            return;
     421        Point2D.Double zoomPos = getPosition(mapPoint);
     422        jobDispatcher.cancelOutstandingJobs(); // Clearing outstanding load
     423        // requests
     424        setDisplayPositionByLatLon(mapPoint, zoomPos.x, zoomPos.y, zoom);
     425    }
     426
     427    public void setZoom(int zoom) {
     428        setZoom(zoom, new Point(getWidth() / 2, getHeight() / 2));
     429    }
     430
     431    /**
     432     * retrieves a tile from the cache. If the tile is not present in the cache
     433     * a load job is added to the working queue of {@link JobThread}.
     434     *
     435     * @param tilex
     436     * @param tiley
     437     * @param zoom
     438     * @return specified tile from the cache or <code>null</code> if the tile
     439     *         was not found in the cache.
     440     */
     441    protected Tile getTile(int tilex, int tiley, int zoom) {
     442        int max = (1 << zoom);
     443        if (tilex < 0 || tilex >= max || tiley < 0 || tiley >= max)
     444            return null;
     445        Tile tile = tileCache.getTile(tileSource, tilex, tiley, zoom);
     446        if (tile == null) {
     447            tile = new Tile(tileSource, tilex, tiley, zoom);
     448            tileCache.addTile(tile);
     449            tile.loadPlaceholderFromCache(tileCache);
     450        }
     451        if (!tile.isLoaded()) {
     452            jobDispatcher.addJob(tileLoader.createTileLoaderJob(tileSource, tilex, tiley, zoom));
     453        }
     454        return tile;
     455    }
     456
     457    /**
     458     * Every time the zoom level changes this method is called. Override it in
     459     * derived implementations for adapting zoom dependent values. The new zoom
     460     * level can be obtained via {@link #getZoom()}.
     461     *
     462     * @param oldZoom
     463     *            the previous zoom level
     464     */
     465    protected void zoomChanged(int oldZoom) {
     466        zoomSlider.setToolTipText("Zoom level " + zoom);
     467        zoomInButton.setToolTipText("Zoom to level " + (zoom + 1));
     468        zoomOutButton.setToolTipText("Zoom to level " + (zoom - 1));
     469        zoomOutButton.setEnabled(zoom > tileSource.getMinZoom());
     470        zoomInButton.setEnabled(zoom < tileSource.getMaxZoom());
     471    }
     472
     473    public boolean isTileGridVisible() {
     474        return tileGridVisible;
     475    }
     476
     477    public void setTileGridVisible(boolean tileGridVisible) {
     478        this.tileGridVisible = tileGridVisible;
     479        repaint();
     480    }
     481
     482    public boolean getMapMarkersVisible() {
     483        return mapMarkersVisible;
     484    }
     485
     486    /**
     487     * Enables or disables painting of the {@link MapMarker}
     488     *
     489     * @param mapMarkersVisible
     490     * @see #addMapMarker(MapMarker)
     491     * @see #getMapMarkerList()
     492     */
     493    public void setMapMarkerVisible(boolean mapMarkersVisible) {
     494        this.mapMarkersVisible = mapMarkersVisible;
     495        repaint();
     496    }
     497
     498    public void setMapMarkerList(List<MapMarker> mapMarkerList) {
     499        this.mapMarkerList = mapMarkerList;
     500        repaint();
     501    }
     502
     503    public List<MapMarker> getMapMarkerList() {
     504        return mapMarkerList;
     505    }
     506
     507    public void addMapMarker(MapMarker marker) {
     508        mapMarkerList.add(marker);
     509    }
     510
     511    public void setZoomContolsVisible(boolean visible) {
     512        zoomSlider.setVisible(visible);
     513        zoomInButton.setVisible(visible);
     514        zoomOutButton.setVisible(visible);
     515    }
     516
     517    public boolean getZoomContolsVisible() {
     518        return zoomSlider.isVisible();
     519    }
     520
     521    public TileCache getTileCache() {
     522        return tileCache;
     523    }
     524
     525    public TileLoader getTileLoader() {
     526        return tileLoader;
     527    }
     528
     529    public void setTileLoader(TileLoader tileLoader) {
     530        this.tileLoader = tileLoader;
     531    }
     532
     533    public TileSource getTileLayerSource() {
     534        return tileSource;
     535    }
     536
     537    public TileSource getTileSource() {
     538        return tileSource;
     539    }
     540
     541    public void setTileSource(TileSource tileSource) {
     542        if (tileSource.getMaxZoom() > MAX_ZOOM)
     543            throw new RuntimeException("Maximum zoom level too high");
     544        if (tileSource.getMinZoom() < MIN_ZOOM)
     545            throw new RuntimeException("Minumim zoom level too low");
     546        this.tileSource = tileSource;
     547        zoomSlider.setMinimum(tileSource.getMinZoom());
     548        zoomSlider.setMaximum(tileSource.getMaxZoom());
     549        jobDispatcher.cancelOutstandingJobs();
     550        if (zoom > tileSource.getMaxZoom())
     551            setZoom(tileSource.getMaxZoom());
     552        repaint();
     553    }
     554
     555    public void tileLoadingFinished(Tile tile, boolean success) {
     556        repaint();
     557    }
    572558
    573559}
Note: See TracChangeset for help on using the changeset viewer.