Changeset 11783 in osm for applications/viewer/jmapviewer/src/org
- Timestamp:
- 2008-11-07T15:23:05+01:00 (16 years ago)
- Location:
- applications/viewer/jmapviewer
- Files:
-
- 1 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/viewer/jmapviewer
- Property svn:ignore
-
old new 1 tiles 1 2 JMapViewer_Demo.jar 2 3 JMapViewer_src.jar
-
- Property svn:ignore
-
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java
r11104 r11783 11 11 import java.awt.event.ActionListener; 12 12 import java.awt.geom.Point2D; 13 import java.awt.image.BufferedImage;14 13 import java.util.LinkedList; 15 14 import java.util.List; 16 15 17 import javax.imageio.ImageIO;18 16 import javax.swing.ImageIcon; 19 17 import javax.swing.JButton; … … 40 38 public class JMapViewer extends JPanel implements TileLoaderListener { 41 39 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 } 572 558 573 559 } -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmFileCacheTileLoader.java
r11103 r11783 14 14 import java.net.URLConnection; 15 15 import java.nio.charset.Charset; 16 import java.util.logging.Logger; 16 17 17 18 import org.openstreetmap.gui.jmapviewer.interfaces.TileCache; … … 30 31 public class OsmFileCacheTileLoader extends OsmTileLoader { 31 32 32 private static final String ETAG_FILE_EXT = ".etag"; 33 34 private static final Charset ETAG_CHARSET = Charset.forName("UTF-8"); 35 36 public static final long FILE_AGE_ONE_DAY = 1000 * 60 * 60 * 24; 37 public static final long FILE_AGE_ONE_WEEK = FILE_AGE_ONE_DAY * 7; 38 39 protected String cacheDirBase; 40 41 protected long maxCacheFileAge = FILE_AGE_ONE_WEEK; 42 protected long recheckAfter = FILE_AGE_ONE_DAY; 43 44 public OsmFileCacheTileLoader(TileLoaderListener map) { 45 super(map); 46 String tempDir = System.getProperty("java.io.tmpdir"); 47 try { 48 if (tempDir == null) 49 throw new IOException(); 50 File cacheDir = new File(tempDir, "JMapViewerTiles"); 51 // System.out.println(cacheDir); 52 if (!cacheDir.exists() && !cacheDir.mkdirs()) 53 throw new IOException(); 54 cacheDirBase = cacheDir.getAbsolutePath(); 55 } catch (Exception e) { 56 cacheDirBase = "tiles"; 57 } 58 } 59 60 public Runnable createTileLoaderJob(final TileSource source, 61 final int tilex, final int tiley, final int zoom) { 62 return new FileLoadJob(source, tilex, tiley, zoom); 63 } 64 65 protected class FileLoadJob implements Runnable { 66 InputStream input = null; 67 68 int tilex, tiley, zoom; 69 Tile tile; 70 TileSource source; 71 File tileCacheDir; 72 File tileFile = null; 73 long fileAge = 0; 74 boolean fileTilePainted = false; 75 76 public FileLoadJob(TileSource source, int tilex, int tiley, int zoom) { 77 super(); 78 this.source = source; 79 this.tilex = tilex; 80 this.tiley = tiley; 81 this.zoom = zoom; 82 } 83 84 public void run() { 85 TileCache cache = listener.getTileCache(); 86 synchronized (cache) { 87 tile = cache.getTile(source, tilex, tiley, zoom); 88 if (tile == null || tile.isLoaded() || tile.loading) 89 return; 90 tile.loading = true; 91 } 92 tileCacheDir = new File(cacheDirBase, source.getName()); 93 if (!tileCacheDir.exists()) 94 tileCacheDir.mkdirs(); 95 if (loadTileFromFile()) 96 return; 97 if (fileTilePainted) { 98 Runnable job = new Runnable() { 99 100 public void run() { 101 loadorUpdateTile(); 102 } 103 }; 104 JobDispatcher.getInstance().addJob(job); 105 } else { 106 loadorUpdateTile(); 107 } 108 } 109 110 protected void loadorUpdateTile() { 111 112 try { 113 // System.out.println("Loading tile from OSM: " + tile); 114 HttpURLConnection urlConn = loadTileFromOsm(tile); 115 if (tileFile != null) { 116 switch (source.getTileUpdate()) { 117 case IfModifiedSince: 118 urlConn.setIfModifiedSince(fileAge); 119 break; 120 case LastModified: 121 if (!isOsmTileNewer(fileAge)) { 122 // System.out.println( 123 // "LastModified: Local version is up to date: " + 124 // tile); 125 tile.setLoaded(true); 126 tileFile.setLastModified(System.currentTimeMillis() 127 - maxCacheFileAge + recheckAfter); 128 return; 129 } 130 break; 131 } 132 } 133 if (source.getTileUpdate() == TileUpdate.ETag 134 || source.getTileUpdate() == TileUpdate.IfNoneMatch) { 135 if (tileFile != null) { 136 String fileETag = loadETagfromFile(); 137 if (fileETag != null) { 138 switch (source.getTileUpdate()) { 139 case IfNoneMatch: 140 urlConn.addRequestProperty("If-None-Match", 141 fileETag); 142 break; 143 case ETag: 144 if (hasOsmTileETag(fileETag)) { 145 tile.setLoaded(true); 146 tileFile.setLastModified(System 147 .currentTimeMillis() 148 - maxCacheFileAge + recheckAfter); 149 return; 150 } 151 } 152 } 153 } 154 155 String eTag = urlConn.getHeaderField("ETag"); 156 saveETagToFile(eTag); 157 } 158 if (urlConn.getResponseCode() == 304) { 159 // If we are isModifiedSince or If-None-Match has been set 160 // and the server answers with a HTTP 304 = "Not Modified" 161 System.out.println("Local version is up to date: " + tile); 162 tile.setLoaded(true); 163 tileFile.setLastModified(System.currentTimeMillis() 164 - maxCacheFileAge + recheckAfter); 165 return; 166 } 167 168 byte[] buffer = loadTileInBuffer(urlConn); 169 if (buffer != null) { 170 tile.loadImage(new ByteArrayInputStream(buffer)); 171 tile.setLoaded(true); 172 listener.tileLoadingFinished(tile); 173 saveTileToFile(buffer); 174 } else { 175 tile.setLoaded(true); 176 } 177 } catch (Exception e) { 178 if (input == null /* || !input.isStopped() */) 179 System.err.println("failed loading " + zoom + "/" + tilex 180 + "/" + tiley + " " + e.getMessage()); 181 } finally { 182 tile.loading = false; 183 } 184 } 185 186 protected boolean loadTileFromFile() { 187 FileInputStream fin = null; 188 try { 189 tileFile = getTileFile(); 190 fin = new FileInputStream(tileFile); 191 if (fin.available() == 0) 192 throw new IOException("File empty"); 193 tile.loadImage(fin); 194 fin.close(); 195 fileAge = tileFile.lastModified(); 196 boolean oldTile = System.currentTimeMillis() - fileAge > maxCacheFileAge; 197 // System.out.println("Loaded from file: " + tile); 198 if (!oldTile) { 199 tile.setLoaded(true); 200 listener.tileLoadingFinished(tile); 201 fileTilePainted = true; 202 return true; 203 } 204 listener.tileLoadingFinished(tile); 205 fileTilePainted = true; 206 } catch (Exception e) { 207 try { 208 if (fin != null) { 209 fin.close(); 210 tileFile.delete(); 211 } 212 } catch (Exception e1) { 213 } 214 tileFile = null; 215 fileAge = 0; 216 } 217 return false; 218 } 219 220 protected byte[] loadTileInBuffer(URLConnection urlConn) 221 throws IOException { 222 input = urlConn.getInputStream(); 223 ByteArrayOutputStream bout = new ByteArrayOutputStream(input 224 .available()); 225 byte[] buffer = new byte[2048]; 226 boolean finished = false; 227 do { 228 int read = input.read(buffer); 229 if (read >= 0) 230 bout.write(buffer, 0, read); 231 else 232 finished = true; 233 } while (!finished); 234 if (bout.size() == 0) 235 return null; 236 return bout.toByteArray(); 237 } 238 239 /** 240 * Performs a <code>HEAD</code> request for retrieving the 241 * <code>LastModified</code> header value. 242 * 243 * Note: This does only work with servers providing the 244 * <code>LastModified</code> header: 245 * <ul> 246 * <li>{@link OsmTileLoader#MAP_OSMA} - supported</li> 247 * <li>{@link OsmTileLoader#MAP_MAPNIK} - not supported</li> 248 * </ul> 249 * 250 * @param fileAge 251 * @return <code>true</code> if the tile on the server is newer than the 252 * file 253 * @throws IOException 254 */ 255 protected boolean isOsmTileNewer(long fileAge) throws IOException { 256 URL url; 257 url = new URL(tile.getUrl()); 258 HttpURLConnection urlConn = (HttpURLConnection) url 259 .openConnection(); 260 urlConn.setRequestMethod("HEAD"); 261 urlConn.setReadTimeout(30000); // 30 seconds read timeout 262 // System.out.println("Tile age: " + new 263 // Date(urlConn.getLastModified()) + " / " 264 // + new Date(fileAge)); 265 long lastModified = urlConn.getLastModified(); 266 if (lastModified == 0) 267 return true; // no LastModified time returned 268 return (lastModified > fileAge); 269 } 270 271 protected boolean hasOsmTileETag(String eTag) throws IOException { 272 URL url; 273 url = new URL(tile.getUrl()); 274 HttpURLConnection urlConn = (HttpURLConnection) url 275 .openConnection(); 276 urlConn.setRequestMethod("HEAD"); 277 urlConn.setReadTimeout(30000); // 30 seconds read timeout 278 // System.out.println("Tile age: " + new 279 // Date(urlConn.getLastModified()) + " / " 280 // + new Date(fileAge)); 281 String osmETag = urlConn.getHeaderField("ETag"); 282 if (osmETag == null) 283 return true; 284 return (osmETag.equals(eTag)); 285 } 286 287 protected File getTileFile() throws IOException { 288 return new File(tileCacheDir + "/" + tile.getZoom() + "_" 289 + tile.getXtile() + "_" + tile.getYtile() + "." 290 + source.getTileType()); 291 } 292 293 protected void saveTileToFile(byte[] rawData) { 294 try { 295 FileOutputStream f = new FileOutputStream(tileCacheDir + "/" 296 + tile.getZoom() + "_" + tile.getXtile() + "_" 297 + tile.getYtile() + "." + source.getTileType()); 298 f.write(rawData); 299 f.close(); 300 // System.out.println("Saved tile to file: " + tile); 301 } catch (Exception e) { 302 System.err.println("Failed to save tile content: " 303 + e.getLocalizedMessage()); 304 } 305 } 306 307 protected void saveETagToFile(String eTag) { 308 try { 309 FileOutputStream f = new FileOutputStream(tileCacheDir + "/" 310 + tile.getZoom() + "_" + tile.getXtile() + "_" 311 + tile.getYtile() + ETAG_FILE_EXT); 312 f.write(eTag.getBytes(ETAG_CHARSET.name())); 313 f.close(); 314 } catch (Exception e) { 315 System.err.println("Failed to save ETag: " 316 + e.getLocalizedMessage()); 317 } 318 } 319 320 protected String loadETagfromFile() { 321 try { 322 FileInputStream f = new FileInputStream(tileCacheDir + "/" 323 + tile.getZoom() + "_" + tile.getXtile() + "_" 324 + tile.getYtile() + ETAG_FILE_EXT); 325 byte[] buf = new byte[f.available()]; 326 f.read(buf); 327 f.close(); 328 return new String(buf, ETAG_CHARSET.name()); 329 } catch (Exception e) { 330 return null; 331 } 332 } 333 334 } 335 336 public long getMaxFileAge() { 337 return maxCacheFileAge; 338 } 339 340 /** 341 * Sets the maximum age of the local cached tile in the file system. If a 342 * local tile is older than the specified file age 343 * {@link OsmFileCacheTileLoader} will connect to the tile server and check 344 * if a newer tile is available using the mechanism specified for the 345 * selected tile source/server. 346 * 347 * @param maxFileAge 348 * maximum age in milliseconds 349 * @see #FILE_AGE_ONE_DAY 350 * @see #FILE_AGE_ONE_WEEK 351 * @see TileSource#getTileUpdate() 352 */ 353 public void setCacheMaxFileAge(long maxFileAge) { 354 this.maxCacheFileAge = maxFileAge; 355 } 356 357 public String getCacheDirBase() { 358 return cacheDirBase; 359 } 360 361 public void setTileCacheDir(String tileCacheDir) { 362 File dir = new File(tileCacheDir); 363 dir.mkdirs(); 364 this.cacheDirBase = dir.getAbsolutePath(); 365 } 33 private static final Logger log = Logger.getLogger(OsmFileCacheTileLoader.class.getName()); 34 35 private static final String ETAG_FILE_EXT = ".etag"; 36 37 private static final Charset ETAG_CHARSET = Charset.forName("UTF-8"); 38 39 public static final long FILE_AGE_ONE_DAY = 1000 * 60 * 60 * 24; 40 public static final long FILE_AGE_ONE_WEEK = FILE_AGE_ONE_DAY * 7; 41 42 protected String cacheDirBase; 43 44 protected long maxCacheFileAge = FILE_AGE_ONE_WEEK; 45 protected long recheckAfter = FILE_AGE_ONE_DAY; 46 47 public OsmFileCacheTileLoader(TileLoaderListener map) { 48 super(map); 49 String tempDir = System.getProperty("java.io.tmpdir"); 50 try { 51 if (tempDir == null) 52 throw new IOException(); 53 File cacheDir = new File(tempDir, "JMapViewerTiles"); 54 log.finest("Tile cache directory: " + cacheDir); 55 if (!cacheDir.exists() && !cacheDir.mkdirs()) 56 throw new IOException(); 57 cacheDirBase = cacheDir.getAbsolutePath(); 58 } catch (Exception e) { 59 cacheDirBase = "tiles"; 60 } 61 } 62 63 public Runnable createTileLoaderJob(final TileSource source, final int tilex, final int tiley, final int zoom) { 64 return new FileLoadJob(source, tilex, tiley, zoom); 65 } 66 67 protected class FileLoadJob implements Runnable { 68 InputStream input = null; 69 70 int tilex, tiley, zoom; 71 Tile tile; 72 TileSource source; 73 File tileCacheDir; 74 File tileFile = null; 75 long fileAge = 0; 76 boolean fileTilePainted = false; 77 78 public FileLoadJob(TileSource source, int tilex, int tiley, int zoom) { 79 super(); 80 this.source = source; 81 this.tilex = tilex; 82 this.tiley = tiley; 83 this.zoom = zoom; 84 } 85 86 public void run() { 87 TileCache cache = listener.getTileCache(); 88 synchronized (cache) { 89 tile = cache.getTile(source, tilex, tiley, zoom); 90 if (tile == null || tile.isLoaded() || tile.loading) 91 return; 92 tile.loading = true; 93 } 94 tileCacheDir = new File(cacheDirBase, source.getName()); 95 if (!tileCacheDir.exists()) 96 tileCacheDir.mkdirs(); 97 if (loadTileFromFile()) 98 return; 99 if (fileTilePainted) { 100 Runnable job = new Runnable() { 101 102 public void run() { 103 loadOrUpdateTile(); 104 } 105 }; 106 JobDispatcher.getInstance().addJob(job); 107 } else { 108 loadOrUpdateTile(); 109 } 110 } 111 112 protected void loadOrUpdateTile() { 113 114 try { 115 // log.finest("Loading tile from OSM: " + tile); 116 HttpURLConnection urlConn = loadTileFromOsm(tile); 117 if (tileFile != null) { 118 switch (source.getTileUpdate()) { 119 case IfModifiedSince: 120 urlConn.setIfModifiedSince(fileAge); 121 break; 122 case LastModified: 123 if (!isOsmTileNewer(fileAge)) { 124 log.finest("LastModified test: local version is up to date: " + tile); 125 tile.setLoaded(true); 126 tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge + recheckAfter); 127 return; 128 } 129 break; 130 } 131 } 132 if (source.getTileUpdate() == TileUpdate.ETag || source.getTileUpdate() == TileUpdate.IfNoneMatch) { 133 if (tileFile != null) { 134 String fileETag = loadETagfromFile(); 135 if (fileETag != null) { 136 switch (source.getTileUpdate()) { 137 case IfNoneMatch: 138 urlConn.addRequestProperty("If-None-Match", fileETag); 139 break; 140 case ETag: 141 if (hasOsmTileETag(fileETag)) { 142 tile.setLoaded(true); 143 tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge 144 + recheckAfter); 145 return; 146 } 147 } 148 } 149 } 150 151 String eTag = urlConn.getHeaderField("ETag"); 152 saveETagToFile(eTag); 153 } 154 if (urlConn.getResponseCode() == 304) { 155 // If we are isModifiedSince or If-None-Match has been set 156 // and the server answers with a HTTP 304 = "Not Modified" 157 log.finest("ETag test: local version is up to date: " + tile); 158 tile.setLoaded(true); 159 tileFile.setLastModified(System.currentTimeMillis() - maxCacheFileAge + recheckAfter); 160 return; 161 } 162 163 byte[] buffer = loadTileInBuffer(urlConn); 164 if (buffer != null) { 165 tile.loadImage(new ByteArrayInputStream(buffer)); 166 tile.setLoaded(true); 167 listener.tileLoadingFinished(tile, true); 168 saveTileToFile(buffer); 169 } else { 170 tile.setLoaded(true); 171 } 172 } catch (Exception e) { 173 tile.setImage(Tile.ERROR_IMAGE); 174 listener.tileLoadingFinished(tile, false); 175 if (input == null) 176 System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley + " " + e.getMessage()); 177 } finally { 178 tile.loading = false; 179 tile.setLoaded(true); 180 } 181 } 182 183 protected boolean loadTileFromFile() { 184 FileInputStream fin = null; 185 try { 186 tileFile = getTileFile(); 187 fin = new FileInputStream(tileFile); 188 if (fin.available() == 0) 189 throw new IOException("File empty"); 190 tile.loadImage(fin); 191 fin.close(); 192 fileAge = tileFile.lastModified(); 193 boolean oldTile = System.currentTimeMillis() - fileAge > maxCacheFileAge; 194 // System.out.println("Loaded from file: " + tile); 195 if (!oldTile) { 196 tile.setLoaded(true); 197 listener.tileLoadingFinished(tile, true); 198 fileTilePainted = true; 199 return true; 200 } 201 listener.tileLoadingFinished(tile, true); 202 fileTilePainted = true; 203 } catch (Exception e) { 204 try { 205 if (fin != null) { 206 fin.close(); 207 tileFile.delete(); 208 } 209 } catch (Exception e1) { 210 } 211 tileFile = null; 212 fileAge = 0; 213 } 214 return false; 215 } 216 217 protected byte[] loadTileInBuffer(URLConnection urlConn) throws IOException { 218 input = urlConn.getInputStream(); 219 ByteArrayOutputStream bout = new ByteArrayOutputStream(input.available()); 220 byte[] buffer = new byte[2048]; 221 boolean finished = false; 222 do { 223 int read = input.read(buffer); 224 if (read >= 0) 225 bout.write(buffer, 0, read); 226 else 227 finished = true; 228 } while (!finished); 229 if (bout.size() == 0) 230 return null; 231 return bout.toByteArray(); 232 } 233 234 /** 235 * Performs a <code>HEAD</code> request for retrieving the 236 * <code>LastModified</code> header value. 237 * 238 * Note: This does only work with servers providing the 239 * <code>LastModified</code> header: 240 * <ul> 241 * <li>{@link OsmTileLoader#MAP_OSMA} - supported</li> 242 * <li>{@link OsmTileLoader#MAP_MAPNIK} - not supported</li> 243 * </ul> 244 * 245 * @param fileAge 246 * @return <code>true</code> if the tile on the server is newer than the 247 * file 248 * @throws IOException 249 */ 250 protected boolean isOsmTileNewer(long fileAge) throws IOException { 251 URL url; 252 url = new URL(tile.getUrl()); 253 HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); 254 urlConn.setRequestMethod("HEAD"); 255 urlConn.setReadTimeout(30000); // 30 seconds read timeout 256 // System.out.println("Tile age: " + new 257 // Date(urlConn.getLastModified()) + " / " 258 // + new Date(fileAge)); 259 long lastModified = urlConn.getLastModified(); 260 if (lastModified == 0) 261 return true; // no LastModified time returned 262 return (lastModified > fileAge); 263 } 264 265 protected boolean hasOsmTileETag(String eTag) throws IOException { 266 URL url; 267 url = new URL(tile.getUrl()); 268 HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); 269 urlConn.setRequestMethod("HEAD"); 270 urlConn.setReadTimeout(30000); // 30 seconds read timeout 271 // System.out.println("Tile age: " + new 272 // Date(urlConn.getLastModified()) + " / " 273 // + new Date(fileAge)); 274 String osmETag = urlConn.getHeaderField("ETag"); 275 if (osmETag == null) 276 return true; 277 return (osmETag.equals(eTag)); 278 } 279 280 protected File getTileFile() throws IOException { 281 return new File(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() + "_" + tile.getYtile() + "." 282 + source.getTileType()); 283 } 284 285 protected void saveTileToFile(byte[] rawData) { 286 try { 287 FileOutputStream f = new FileOutputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() 288 + "_" + tile.getYtile() + "." + source.getTileType()); 289 f.write(rawData); 290 f.close(); 291 // System.out.println("Saved tile to file: " + tile); 292 } catch (Exception e) { 293 System.err.println("Failed to save tile content: " + e.getLocalizedMessage()); 294 } 295 } 296 297 protected void saveETagToFile(String eTag) { 298 try { 299 FileOutputStream f = new FileOutputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() 300 + "_" + tile.getYtile() + ETAG_FILE_EXT); 301 f.write(eTag.getBytes(ETAG_CHARSET.name())); 302 f.close(); 303 } catch (Exception e) { 304 System.err.println("Failed to save ETag: " + e.getLocalizedMessage()); 305 } 306 } 307 308 protected String loadETagfromFile() { 309 try { 310 FileInputStream f = new FileInputStream(tileCacheDir + "/" + tile.getZoom() + "_" + tile.getXtile() 311 + "_" + tile.getYtile() + ETAG_FILE_EXT); 312 byte[] buf = new byte[f.available()]; 313 f.read(buf); 314 f.close(); 315 return new String(buf, ETAG_CHARSET.name()); 316 } catch (Exception e) { 317 return null; 318 } 319 } 320 321 } 322 323 public long getMaxFileAge() { 324 return maxCacheFileAge; 325 } 326 327 /** 328 * Sets the maximum age of the local cached tile in the file system. If a 329 * local tile is older than the specified file age 330 * {@link OsmFileCacheTileLoader} will connect to the tile server and check 331 * if a newer tile is available using the mechanism specified for the 332 * selected tile source/server. 333 * 334 * @param maxFileAge 335 * maximum age in milliseconds 336 * @see #FILE_AGE_ONE_DAY 337 * @see #FILE_AGE_ONE_WEEK 338 * @see TileSource#getTileUpdate() 339 */ 340 public void setCacheMaxFileAge(long maxFileAge) { 341 this.maxCacheFileAge = maxFileAge; 342 } 343 344 public String getCacheDirBase() { 345 return cacheDirBase; 346 } 347 348 public void setTileCacheDir(String tileCacheDir) { 349 File dir = new File(tileCacheDir); 350 dir.mkdirs(); 351 this.cacheDirBase = dir.getAbsolutePath(); 352 } 366 353 367 354 } -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmTileLoader.java
r11103 r11783 20 20 public class OsmTileLoader implements TileLoader { 21 21 22 22 protected TileLoaderListener listener; 23 23 24 25 26 24 public OsmTileLoader(TileLoaderListener listener) { 25 this.listener = listener; 26 } 27 27 28 public Runnable createTileLoaderJob(final TileSource source, final int tilex, final int tiley, 29 final int zoom) { 30 return new Runnable() { 28 public Runnable createTileLoaderJob(final TileSource source, final int tilex, final int tiley, final int zoom) { 29 return new Runnable() { 31 30 32 31 InputStream input = null; 33 32 34 public void run() { 35 TileCache cache = listener.getTileCache(); 36 Tile tile; 37 synchronized (cache) { 38 tile = cache.getTile(source, tilex, tiley, zoom); 39 if (tile == null || tile.isLoaded() || tile.loading) 40 return; 41 tile.loading = true; 42 } 43 try { 44 // Thread.sleep(500); 45 input = loadTileFromOsm(tile).getInputStream(); 46 tile.loadImage(input); 47 tile.setLoaded(true); 48 listener.tileLoadingFinished(tile); 49 input.close(); 50 input = null; 51 } catch (Exception e) { 52 if (input == null /* || !input.isStopped() */) 53 System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley 54 + " " + e.getMessage()); 55 } finally { 56 tile.loading = false; 57 } 58 } 33 public void run() { 34 TileCache cache = listener.getTileCache(); 35 Tile tile; 36 synchronized (cache) { 37 tile = cache.getTile(source, tilex, tiley, zoom); 38 if (tile == null || tile.isLoaded() || tile.loading) 39 return; 40 tile.loading = true; 41 } 42 try { 43 // Thread.sleep(500); 44 input = loadTileFromOsm(tile).getInputStream(); 45 tile.loadImage(input); 46 tile.setLoaded(true); 47 listener.tileLoadingFinished(tile, true); 48 input.close(); 49 input = null; 50 } catch (Exception e) { 51 tile.setImage(Tile.ERROR_IMAGE); 52 listener.tileLoadingFinished(tile, false); 53 if (input == null) 54 System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley + " " + e.getMessage()); 55 } finally { 56 tile.loading = false; 57 tile.setLoaded(true); 58 } 59 } 59 60 60 61 61 }; 62 } 62 63 63 64 65 66 67 68 69 70 64 protected HttpURLConnection loadTileFromOsm(Tile tile) throws IOException { 65 URL url; 66 url = new URL(tile.getUrl()); 67 HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); 68 urlConn.setReadTimeout(30000); // 30 seconds read 69 // timeout 70 return urlConn; 71 } 71 72 72 73 74 75 76 73 @Override 74 public String toString() { 75 return getClass().getSimpleName(); 76 } 77 77 78 } -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/OsmTileSource.java
r11103 r11783 14 14 return 18; 15 15 } 16 17 public int getMinZoom() { 18 return 0; 19 } 16 20 17 21 public String getTileUrl(int zoom, int tilex, int tiley) { -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java
r9784 r11783 23 23 public class Tile { 24 24 25 protected TileSource source; 26 protected int xtile; 27 protected int ytile; 28 protected int zoom; 29 protected BufferedImage image; 30 protected String key; 31 protected boolean loaded = false; 32 protected boolean loading = false; 33 public static final int SIZE = 256; 34 35 /** 36 * Creates a tile with empty image. 37 * 38 * @param source 39 * @param xtile 40 * @param ytile 41 * @param zoom 42 */ 43 public Tile(TileSource source, int xtile, int ytile, int zoom) { 44 super(); 45 this.source = source; 46 this.xtile = xtile; 47 this.ytile = ytile; 48 this.zoom = zoom; 49 this.image = null; 50 this.key = getTileKey(source, xtile, ytile, zoom); 51 } 52 53 public Tile(TileSource source, int xtile, int ytile, int zoom, BufferedImage image) { 54 this(source, xtile, ytile, zoom); 55 this.image = image; 56 } 57 58 /** 59 * Tries to get tiles of a lower or higher zoom level (one or two level 60 * difference) from cache and use it as a placeholder until the tile has 61 * been loaded. 62 */ 63 public void loadPlaceholderFromCache(TileCache cache) { 64 BufferedImage tmpImage = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_RGB); 65 Graphics2D g = (Graphics2D) tmpImage.getGraphics(); 66 // g.drawImage(image, 0, 0, null); 67 for (int zoomDiff = 1; zoomDiff < 5; zoomDiff++) { 68 // first we check if there are already the 2^x tiles 69 // of a higher detail level 70 int zoom_high = zoom + zoomDiff; 71 if (zoomDiff < 3 && zoom_high <= JMapViewer.MAX_ZOOM) { 72 int factor = 1 << zoomDiff; 73 int xtile_high = xtile << zoomDiff; 74 int ytile_high = ytile << zoomDiff; 75 double scale = 1.0 / factor; 76 g.setTransform(AffineTransform.getScaleInstance(scale, scale)); 77 int paintedTileCount = 0; 78 for (int x = 0; x < factor; x++) { 79 for (int y = 0; y < factor; y++) { 80 Tile tile = 81 cache.getTile(source, xtile_high + x, ytile_high + y, zoom_high); 82 if (tile != null && tile.isLoaded()) { 83 paintedTileCount++; 84 tile.paint(g, x * SIZE, y * SIZE); 85 } 86 } 87 } 88 if (paintedTileCount == factor * factor) { 89 image = tmpImage; 90 return; 91 } 92 } 93 94 int zoom_low = zoom - zoomDiff; 95 if (zoom_low >= JMapViewer.MIN_ZOOM) { 96 int xtile_low = xtile >> zoomDiff; 97 int ytile_low = ytile >> zoomDiff; 98 int factor = (1 << zoomDiff); 99 double scale = (double) factor; 100 AffineTransform at = new AffineTransform(); 101 int translate_x = (xtile % factor) * SIZE; 102 int translate_y = (ytile % factor) * SIZE; 103 at.setTransform(scale, 0, 0, scale, -translate_x, -translate_y); 104 g.setTransform(at); 105 Tile tile = cache.getTile(source, xtile_low, ytile_low, zoom_low); 106 if (tile != null && tile.isLoaded()) { 107 tile.paint(g, 0, 0); 108 image = tmpImage; 109 return; 110 } 111 } 112 } 113 } 114 115 public TileSource getSource() { 116 return source; 117 } 118 119 /** 120 * @return tile number on the x axis of this tile 121 */ 122 public int getXtile() { 123 return xtile; 124 } 125 126 /** 127 * @return tile number on the y axis of this tile 128 */ 129 public int getYtile() { 130 return ytile; 131 } 132 133 /** 134 * @return zoom level of this tile 135 */ 136 public int getZoom() { 137 return zoom; 138 } 139 140 public BufferedImage getImage() { 141 return image; 142 } 143 144 public void setImage(BufferedImage image) { 145 this.image = image; 146 } 147 148 public void loadImage(InputStream input) throws IOException { 149 image = ImageIO.read(input); 150 } 151 152 /** 153 * @return key that identifies a tile 154 */ 155 public String getKey() { 156 return key; 157 } 158 159 public boolean isLoaded() { 160 return loaded; 161 } 162 163 public void setLoaded(boolean loaded) { 164 this.loaded = loaded; 165 } 166 167 public String getUrl() { 168 return source.getTileUrl(zoom, xtile, ytile); 169 } 170 171 /** 172 * Paints the tile-image on the {@link Graphics} <code>g</code> at the 173 * position <code>x</code>/<code>y</code>. 174 * 175 * @param g 176 * @param x 177 * x-coordinate in <code>g</code> 178 * @param y 179 * y-coordinate in <code>g</code> 180 */ 181 public void paint(Graphics g, int x, int y) { 182 if (image == null) 183 return; 184 g.drawImage(image, x, y, null); 185 } 186 187 @Override 188 public String toString() { 189 return "Tile " + key; 190 } 191 192 @Override 193 public boolean equals(Object obj) { 194 if (!(obj instanceof Tile)) 195 return false; 196 Tile tile = (Tile) obj; 197 return (xtile == tile.xtile) && (ytile == tile.ytile) && (zoom == tile.zoom); 198 } 199 200 public static String getTileKey(TileSource source, int xtile, int ytile, int zoom) { 201 return zoom + "/" + xtile + "/" + ytile + "@" + source.getName(); 202 } 25 /** 26 * Hourglass image that is displayed until a map tile has been loaded 27 */ 28 public static BufferedImage LOADING_IMAGE; 29 public static BufferedImage ERROR_IMAGE; 30 31 static { 32 try { 33 LOADING_IMAGE = ImageIO.read(JMapViewer.class.getResourceAsStream("images/hourglass.png")); 34 ERROR_IMAGE = ImageIO.read(JMapViewer.class.getResourceAsStream("images/error.png")); 35 } catch (Exception e1) { 36 LOADING_IMAGE = null; 37 ERROR_IMAGE = null; 38 } 39 } 40 41 protected TileSource source; 42 protected int xtile; 43 protected int ytile; 44 protected int zoom; 45 protected BufferedImage image; 46 protected String key; 47 protected boolean loaded = false; 48 protected boolean loading = false; 49 public static final int SIZE = 256; 50 51 /** 52 * Creates a tile with empty image. 53 * 54 * @param source 55 * @param xtile 56 * @param ytile 57 * @param zoom 58 */ 59 public Tile(TileSource source, int xtile, int ytile, int zoom) { 60 super(); 61 this.source = source; 62 this.xtile = xtile; 63 this.ytile = ytile; 64 this.zoom = zoom; 65 this.image = LOADING_IMAGE; 66 this.key = getTileKey(source, xtile, ytile, zoom); 67 } 68 69 public Tile(TileSource source, int xtile, int ytile, int zoom, BufferedImage image) { 70 this(source, xtile, ytile, zoom); 71 this.image = image; 72 } 73 74 /** 75 * Tries to get tiles of a lower or higher zoom level (one or two level 76 * difference) from cache and use it as a placeholder until the tile has 77 * been loaded. 78 */ 79 public void loadPlaceholderFromCache(TileCache cache) { 80 BufferedImage tmpImage = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_RGB); 81 Graphics2D g = (Graphics2D) tmpImage.getGraphics(); 82 // g.drawImage(image, 0, 0, null); 83 for (int zoomDiff = 1; zoomDiff < 5; zoomDiff++) { 84 // first we check if there are already the 2^x tiles 85 // of a higher detail level 86 int zoom_high = zoom + zoomDiff; 87 if (zoomDiff < 3 && zoom_high <= JMapViewer.MAX_ZOOM) { 88 int factor = 1 << zoomDiff; 89 int xtile_high = xtile << zoomDiff; 90 int ytile_high = ytile << zoomDiff; 91 double scale = 1.0 / factor; 92 g.setTransform(AffineTransform.getScaleInstance(scale, scale)); 93 int paintedTileCount = 0; 94 for (int x = 0; x < factor; x++) { 95 for (int y = 0; y < factor; y++) { 96 Tile tile = cache.getTile(source, xtile_high + x, ytile_high + y, zoom_high); 97 if (tile != null && tile.isLoaded()) { 98 paintedTileCount++; 99 tile.paint(g, x * SIZE, y * SIZE); 100 } 101 } 102 } 103 if (paintedTileCount == factor * factor) { 104 image = tmpImage; 105 return; 106 } 107 } 108 109 int zoom_low = zoom - zoomDiff; 110 if (zoom_low >= JMapViewer.MIN_ZOOM) { 111 int xtile_low = xtile >> zoomDiff; 112 int ytile_low = ytile >> zoomDiff; 113 int factor = (1 << zoomDiff); 114 double scale = (double) factor; 115 AffineTransform at = new AffineTransform(); 116 int translate_x = (xtile % factor) * SIZE; 117 int translate_y = (ytile % factor) * SIZE; 118 at.setTransform(scale, 0, 0, scale, -translate_x, -translate_y); 119 g.setTransform(at); 120 Tile tile = cache.getTile(source, xtile_low, ytile_low, zoom_low); 121 if (tile != null && tile.isLoaded()) { 122 tile.paint(g, 0, 0); 123 image = tmpImage; 124 return; 125 } 126 } 127 } 128 } 129 130 public TileSource getSource() { 131 return source; 132 } 133 134 /** 135 * @return tile number on the x axis of this tile 136 */ 137 public int getXtile() { 138 return xtile; 139 } 140 141 /** 142 * @return tile number on the y axis of this tile 143 */ 144 public int getYtile() { 145 return ytile; 146 } 147 148 /** 149 * @return zoom level of this tile 150 */ 151 public int getZoom() { 152 return zoom; 153 } 154 155 public BufferedImage getImage() { 156 return image; 157 } 158 159 public void setImage(BufferedImage image) { 160 this.image = image; 161 } 162 163 public void loadImage(InputStream input) throws IOException { 164 image = ImageIO.read(input); 165 } 166 167 /** 168 * @return key that identifies a tile 169 */ 170 public String getKey() { 171 return key; 172 } 173 174 public boolean isLoaded() { 175 return loaded; 176 } 177 178 public void setLoaded(boolean loaded) { 179 this.loaded = loaded; 180 } 181 182 public String getUrl() { 183 return source.getTileUrl(zoom, xtile, ytile); 184 } 185 186 /** 187 * Paints the tile-image on the {@link Graphics} <code>g</code> at the 188 * position <code>x</code>/<code>y</code>. 189 * 190 * @param g 191 * @param x 192 * x-coordinate in <code>g</code> 193 * @param y 194 * y-coordinate in <code>g</code> 195 */ 196 public void paint(Graphics g, int x, int y) { 197 if (image == null) 198 return; 199 g.drawImage(image, x, y, null); 200 } 201 202 @Override 203 public String toString() { 204 return "Tile " + key; 205 } 206 207 @Override 208 public boolean equals(Object obj) { 209 if (!(obj instanceof Tile)) 210 return false; 211 Tile tile = (Tile) obj; 212 return (xtile == tile.xtile) && (ytile == tile.ytile) && (zoom == tile.zoom); 213 } 214 215 public static String getTileKey(TileSource source, int xtile, int ytile, int zoom) { 216 return zoom + "/" + xtile + "/" + ytile + "@" + source.getName(); 217 } 203 218 204 219 } -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileLoaderListener.java
r11103 r11783 13 13 * @param tile 14 14 */ 15 public void tileLoadingFinished(Tile tile );15 public void tileLoadingFinished(Tile tile, boolean success); 16 16 17 17 public TileCache getTileCache(); -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/interfaces/TileSource.java
r11103 r11783 11 11 public interface TileSource { 12 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 13 /** 14 * Specifies the different mechanisms for detecting updated tiles 15 * respectively only download newer tiles than those stored locally. 16 * 17 * <ul> 18 * <li>{@link #IfNoneMatch} Server provides ETag header entry for all tiles 19 * and <b>supports</b> conditional download via <code>If-None-Match</code> 20 * header entry.</li> 21 * <li>{@link #ETag} Server provides ETag header entry for all tiles but 22 * <b>does not support</b> conditional download via 23 * <code>If-None-Match</code> header entry.</li> 24 * <li>{@link #IfModifiedSince} Server provides Last-Modified header entry 25 * for all tiles and <b>supports</b> conditional download via 26 * <code>If-Modified-Since</code> header entry.</li> 27 * <li>{@link #LastModified} Server provides Last-Modified header entry for 28 * all tiles but <b>does not support</b> conditional download via 29 * <code>If-Modified-Since</code> header entry.</li> 30 * <li>{@link #None} The server does not support any of the listed 31 * mechanisms.</li> 32 * </ul> 33 * 34 */ 35 public enum TileUpdate { 36 IfNoneMatch, ETag, IfModifiedSince, LastModified, None 37 }; 38 38 39 40 41 42 43 44 45 46 39 /** 40 * Specifies the maximum zoom value. The number of zoom levels is [0.. 41 * {@link #getMaxZoom()}]. 42 * 43 * @return maximum zoom value that has to be smaller or equal to 44 * {@link JMapViewer#MAX_ZOOM} 45 */ 46 public int getMaxZoom(); 47 47 48 /** 49 * @return The supported tile update mechanism 50 * @see TileUpdate 51 */ 52 public TileUpdate getTileUpdate(); 48 /** 49 * Specifies the minimum zoom value. This value is usually 0. 50 * Only for maps that cover a certain region up to a limited zoom level 51 * this method should return a value different than 0. 52 * 53 * @return minimum zoom value - usually 0 54 */ 55 public int getMinZoom(); 53 56 54 /** 55 * A tile layer name has to be unique and has to consist only of characters 56 * valid for filenames. 57 * 58 * @return Name of the tile layer 59 */ 60 public String getName(); 57 /** 58 * @return The supported tile update mechanism 59 * @see TileUpdate 60 */ 61 public TileUpdate getTileUpdate(); 61 62 62 /** 63 * Constructs the tile url. 64 * 65 * @param zoom 66 * @param tilex 67 * @param tiley 68 * @return fully qualified url for downloading the specified tile image 69 */ 70 public String getTileUrl(int zoom, int tilex, int tiley); 63 /** 64 * A tile layer name has to be unique and has to consist only of characters 65 * valid for filenames. 66 * 67 * @return Name of the tile layer 68 */ 69 public String getName(); 71 70 72 /** 73 * Specifies the tile image type. For tiles rendered by Mapnik or 74 * Osmarenderer this is usually <code>"png"</code>. 75 * 76 * @return file extension of the tile image type 77 */ 78 public String getTileType(); 71 /** 72 * Constructs the tile url. 73 * 74 * @param zoom 75 * @param tilex 76 * @param tiley 77 * @return fully qualified url for downloading the specified tile image 78 */ 79 public String getTileUrl(int zoom, int tilex, int tiley); 80 81 /** 82 * Specifies the tile image type. For tiles rendered by Mapnik or 83 * Osmarenderer this is usually <code>"png"</code>. 84 * 85 * @return file extension of the tile image type 86 */ 87 public String getTileType(); 79 88 }
Note:
See TracChangeset
for help on using the changeset viewer.