Changeset 9288 in osm for applications
- Timestamp:
- 2008-07-25T17:13:25+02:00 (16 years ago)
- Location:
- applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer
- Files:
-
- 1 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JMapViewer.java
r9263 r9288 13 13 import java.awt.image.BufferedImage; 14 14 import java.io.IOException; 15 import java.io.InputStream; 16 import java.net.HttpURLConnection; 17 import java.net.URL; 15 18 import java.util.LinkedList; 16 19 import java.util.List; … … 37 40 38 41 private static final long serialVersionUID = 1L; 42 43 /** 44 * Vectors for clock-wise tile painting 45 */ 46 protected static final Point[] move = 47 { new Point(1, 0), new Point(0, 1), new Point(-1, 0), new Point(0, -1) }; 39 48 40 49 public static final int MAX_ZOOM = 18; … … 188 197 return; 189 198 190 // Substract the calculated values so that we get the x/y index of the191 // upper left tile again including fraction where in the tile is our192 // map origin point (0,0)193 194 199 // Get the plain tile number 195 200 Point p = new Point(); … … 279 284 super.paintComponent(g); 280 285 281 // Optimization for loading the centered tile in a lower zoom first 282 if (zoom > MIN_ZOOM) { 283 int center_tx = center.x / Tile.WIDTH; 284 int center_ty = center.y / Tile.HEIGHT; 285 Tile centerTile = tileCache.getTile(center_tx, center_ty, zoom); 286 if (centerTile == null || !centerTile.isLoaded()) { 287 // tile in the center of the screen is not loaded, for faster 288 // displaying anything in the center we first load a tile of a 289 // lower zoom level 290 getTile(center_tx / 2, center_ty / 2, zoom - 1); 286 int iMove = 0; 287 288 int tilex = center.x / Tile.WIDTH; 289 int tiley = center.y / Tile.HEIGHT; 290 int off_x = (center.x % Tile.WIDTH); 291 int off_y = (center.y % Tile.HEIGHT); 292 293 int posx = getWidth() / 2 - off_x; 294 int posy = getHeight() / 2 - off_y; 295 296 int diff_left = off_x; 297 int diff_right = Tile.WIDTH - off_x; 298 int diff_top = off_y; 299 int diff_bottom = Tile.HEIGHT - off_y; 300 301 boolean start_left = diff_left < diff_right; 302 boolean start_top = diff_top < diff_bottom; 303 304 if (start_top) { 305 if (start_left) 306 iMove = 2; 307 else 308 iMove = 3; 309 } else { 310 if (start_left) 311 iMove = 1; 312 else 313 iMove = 0; 314 } // calculate the visibility borders 315 int x_min = -Tile.WIDTH; 316 int y_min = -Tile.HEIGHT; 317 int x_max = getWidth(); 318 int y_max = getHeight(); 319 320 boolean painted = true; 321 int x = 0; 322 while (painted) { 323 painted = false; 324 for (int y = 0; y < 4; y++) { 325 if (y % 2 == 0) 326 x++; 327 for (int z = 0; z < x; z++) { 328 if (x_min <= posx && posx <= x_max && y_min <= posy && posy <= y_max) { // tile 329 // is 330 // visible 331 Tile tile = getTile(tilex, tiley, zoom); 332 if (tile != null) { 333 painted = true; 334 tile.paint(g, posx, posy); 335 if (tileGridVisible) 336 g.drawRect(posx, posy, Tile.WIDTH, Tile.HEIGHT); 337 } 338 } 339 Point p = move[iMove]; 340 posx += p.x * Tile.WIDTH; 341 posy += p.y * Tile.HEIGHT; 342 tilex += p.x; 343 tiley += p.y; 344 } 345 iMove = (iMove + 1) % move.length; 291 346 } 292 347 } 293 // Regular tile painting 294 int left = center.x - getWidth() / 2; 295 int top = center.y - getHeight() / 2; 296 297 int tilex = left / Tile.WIDTH; 298 int tiley = top / Tile.HEIGHT; 299 int off_x = left % Tile.WIDTH; 300 int off_y = top % Tile.HEIGHT; 301 for (int x = -off_x; x < getWidth(); x += Tile.WIDTH) { 302 int tiley_tmp = tiley; 303 for (int y = -off_y; y < getHeight(); y += Tile.HEIGHT) { 304 Tile tile = getTile(tilex, tiley_tmp, zoom); 305 if (tile != null) { 306 tile.paint(g, x, y); 307 if (tileGridVisible) 308 g.drawRect(x, y, Tile.WIDTH, Tile.HEIGHT); 309 } 310 tiley_tmp++; 311 } 312 tilex++; 313 } 314 g.fillOval(getWidth()/2 - 5, getHeight()/2 - 5, 10, 10); 315 g.drawString("Test", 50, 10); 348 g.drawString("Tiles in cache: " + tileCache.getTileCount(), 50, 20); 316 349 if (!mapMarkersVisible || mapMarkerList == null) 317 350 return; … … 394 427 } 395 428 if (!tile.isLoaded()) { 396 jobDispatcher.addJob(new Runnable() { 429 jobDispatcher.addJob(new Job() { 430 431 InputStream input = null; 397 432 398 433 public void run() { 399 434 Tile tile = tileCache.getTile(tilex, tiley, zoom); 400 if (tile .isLoaded())435 if (tile == null || tile.isLoaded()) 401 436 return; 402 437 try { 403 Thread.sleep(500); 404 tile.loadTileImage(); 438 //Thread.sleep(500); 439 URL url; 440 url = new URL("http://tile.openstreetmap.org/" + tile.getKey() + ".png"); 441 HttpURLConnection urlConn = (HttpURLConnection) url.openConnection(); 442 urlConn.setReadTimeout(30000); // 30 seconds read 443 // timeout 444 input = urlConn.getInputStream(); 445 tile.setImage(ImageIO.read(input)); 446 tile.setLoaded(true); 405 447 repaint(); 448 input.close(); 449 input = null; 406 450 } catch (Exception e) { 407 System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley 408 + " " + e.getMessage()); 451 if (input == null /* || !input.isStopped() */) 452 System.err.println("failed loading " + zoom + "/" + tilex + "/" + tiley 453 + " " + e.getMessage()); 454 } 455 } 456 457 /** 458 * Terminating all transfers that are currently in progress 459 */ 460 public void stop() { 461 462 try { 463 // if (input != null) 464 // input.stop(); 465 } catch (Exception e) { 409 466 } 410 467 } -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/JobDispatcher.java
r9095 r9288 16 16 protected BlockingQueue<Runnable> jobQueue = new LinkedBlockingQueue<Runnable>(); 17 17 18 Thread[] threads;18 JobThread[] threads; 19 19 20 20 public JobDispatcher(int threadCound) { 21 threads = new Thread[threadCound];21 threads = new JobThread[threadCound]; 22 22 for (int i = 0; i < threadCound; i++) { 23 23 threads[i] = new JobThread(i + 1); … … 26 26 27 27 /** 28 * Removes all jobs from the queue that are currently not being processed. 28 * Removes all jobs from the queue that are currently not being processed 29 * and stops those currently being processed. 29 30 */ 30 31 public void cancelOutstandingJobs() { … … 32 33 for (int i = 0; i < threads.length; i++) { 33 34 try { 34 threads[i].interrupt(); 35 threads[i] = new JobThread(i + 1); 35 Runnable job = threads[i].getJob(); 36 if ((job != null) && (job instanceof Job)) 37 ((Job) job).stop(); 36 38 } catch (Exception e) { 37 39 e.printStackTrace(); … … 49 51 protected class JobThread extends Thread { 50 52 53 Runnable job; 54 51 55 public JobThread(int threadId) { 52 56 super("OSMJobThread " + threadId); 57 job = null; 53 58 start(); 54 59 } … … 57 62 public void run() { 58 63 while (!isInterrupted()) { 59 Runnable job;60 64 try { 61 65 job = jobQueue.take(); … … 65 69 try { 66 70 job.run(); 71 job = null; 67 72 } catch (Exception e) { 68 73 e.printStackTrace(); … … 70 75 } 71 76 } 77 78 /** 79 * @return the job being executed at the moment or <code>null</code> if 80 * the thread is idle. 81 */ 82 public Runnable getJob() { 83 return job; 84 } 85 72 86 } 73 87 -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/MemoryTileCache.java
r9095 r9288 3 3 //License: GPL. Copyright 2008 by Jan Peter Stotz 4 4 5 import java.util.Comparator;6 5 import java.util.Hashtable; 7 import java.util.Map;8 import java.util.Set;9 import java.util.TreeSet;10 import java.util.Map.Entry;11 6 import java.util.logging.Logger; 12 7 13 8 /** 9 * {@link TileCache} implementation that stores all {@link Tile} objects in 10 * memory up to a certain limit ({@link #getCacheSize()}). If the limit is 11 * exceeded the least recently used {@link Tile} objects will be deleted. 14 12 * 15 13 * @author Jan Peter Stotz … … 17 15 public class MemoryTileCache implements TileCache { 18 16 19 private static final Logger log = Logger.getLogger(MemoryTileCache.class 20 .getName()); 21 22 protected int cacheSizeMax = 200; 17 private static final Logger log = Logger.getLogger(MemoryTileCache.class.getName()); 23 18 24 19 /** 25 * Number of tiles after a cache cleanup via {@link #removeOldTiles()};20 * Default cache size 26 21 */ 27 protected int cacheSize Default = 100;22 protected int cacheSize = 200; 28 23 29 24 protected Hashtable<String, CacheEntry> hashtable; 30 25 31 26 /** 32 * A logical clock that "ticks" every time a tile is retrieved from the 33 * cache 27 * List of all tiles in their last recently used order 34 28 */ 35 protected int currentAccessTime = 0;29 protected CacheLinkedListElement lruTiles; 36 30 37 31 public MemoryTileCache() { 38 hashtable = new Hashtable<String, CacheEntry>(200); 32 hashtable = new Hashtable<String, CacheEntry>(cacheSize); 33 lruTiles = new CacheLinkedListElement(); 39 34 } 40 35 41 public synchronized void addTile(Tile tile) { 42 hashtable.put(tile.getKey(), new CacheEntry(tile, currentAccessTime)); 43 if (hashtable.size() > cacheSizeMax) 36 public void addTile(Tile tile) { 37 CacheEntry entry = new CacheEntry(tile); 38 hashtable.put(tile.getKey(), entry); 39 lruTiles.addFirst(entry); 40 if (hashtable.size() > cacheSize) 44 41 removeOldTiles(); 45 42 } … … 49 46 if (entry == null) 50 47 return null; 51 currentAccessTime++; 52 entry.lastAccess = currentAccessTime; 53 // We are right before an integer overflow!! 54 if (currentAccessTime == Integer.MAX_VALUE) 55 removeOldTiles(); 48 // We don't care about placeholder tiles and hourglass image tiles, the 49 // important tiles are the loaded ones 50 if (entry.tile.isLoaded()) 51 lruTiles.moveElementToFirstPos(entry); 56 52 return entry.tile; 57 53 } 58 54 59 55 /** 60 * Removes the least recently used tiles and rewrites the 61 * {@link CacheEntry#lastAccess} of all remaining entries (-n to 0). 62 * 63 * WARNING: While this method is running modifying the {@link #hashtable} is 64 * forbidden! Therefore this method and {@link #addTile(Tile)} are declared 65 * as synchronized. 56 * Removes the least recently used tiles 66 57 */ 67 protected synchronized void removeOldTiles() { 68 try { 69 Set<Map.Entry<String, CacheEntry>> entries = hashtable.entrySet(); 70 TreeSet<Map.Entry<String, CacheEntry>> sortedEntries; 71 // Sort the entries according to their access time 72 sortedEntries = new TreeSet<Map.Entry<String, CacheEntry>>( 73 new MEComparator()); 74 sortedEntries.addAll(entries); 75 // System.out.println("Tiles in Cache: " + hashtable.size() + 76 // " lru=" + currentAccessTime); 77 int tilecount = 0; 78 for (Map.Entry<String, CacheEntry> entry : sortedEntries) { 79 tilecount++; 80 if (tilecount < cacheSizeDefault) { 81 entry.getValue().lastAccess = -tilecount; 82 } else { 83 // System.out.println("removing entry :" 84 // + entry.getValue().lastAccess); 85 entries.remove(entry); 58 protected void removeOldTiles() { 59 synchronized (lruTiles) { 60 try { 61 while (lruTiles.getElementCount() > cacheSize) { 62 CacheEntry entry = lruTiles.getLastElement(); 63 hashtable.remove(entry.tile.getKey()); 64 lruTiles.removeEntry(entry); 86 65 } 66 } catch (Exception e) { 67 log.warning(e.getMessage()); 87 68 } 88 // We can now safely reset the the logical clock89 currentAccessTime = 1;90 // System.out.println("Tiles in Cache: " + hashtable.size() +91 // " lru=" + currentAccessTime);92 } catch (Exception e) {93 log.severe(e.toString());94 69 } 95 70 } 96 71 97 public int get CacheSizeMax() {98 return cacheSizeMax;72 public int getTileCount() { 73 return hashtable.size(); 99 74 } 100 75 101 public void setCacheSizeMax(int cacheSizeMax) { 102 this.cacheSizeMax = cacheSizeMax; 103 this.cacheSizeDefault = cacheSizeMax / 2; 76 public int getCacheSize() { 77 return cacheSize; 104 78 } 105 79 106 protected static class CacheEntry implements Comparable<CacheEntry> { 107 int lastAccess; 80 /** 81 * Changes the maximum number of {@link Tile} objects that this cache holds. 82 * 83 * @param cacheSize 84 * new maximum number of tiles 85 */ 86 public void setCacheSize(int cacheSize) { 87 this.cacheSize = cacheSize; 88 if (hashtable.size() > cacheSize) 89 removeOldTiles(); 90 } 91 92 /** 93 * Linked list element holding the {@link Tile} and links to the 94 * {@link #next} and {@link #prev} item in the list. 95 */ 96 protected static class CacheEntry { 108 97 Tile tile; 109 98 110 protected CacheEntry(Tile tile, int currentAccessTime) { 99 CacheEntry next; 100 CacheEntry prev; 101 102 protected CacheEntry(Tile tile) { 111 103 this.tile = tile; 112 lastAccess = currentAccessTime;113 104 } 114 105 115 public int compareTo(CacheEntry o) {116 if (lastAccess > o.lastAccess)117 return -1;118 else119 return 1;120 }121 106 } 122 107 123 protected static class MEComparator implements 124 Comparator<Map.Entry<String, CacheEntry>> { 108 /** 109 * Special implementation of a double linked list for {@link CacheEntry} 110 * elements. It supports element removal in constant time - in difference to 111 * the Java implementation which needs O(n). 112 * 113 * @author Jan Peter Stotz 114 */ 115 protected static class CacheLinkedListElement { 116 protected CacheEntry firstElement = null; 117 protected CacheEntry lastElement; 118 protected int elementCount; 125 119 126 public int compare(Entry<String, CacheEntry> o1, 127 Entry<String, CacheEntry> o2) { 128 return o1.getValue().compareTo(o2.getValue()); 120 public CacheLinkedListElement() { 121 elementCount = 0; 122 firstElement = null; 123 lastElement = null; 124 } 125 126 /** 127 * Add the element to the head of the list. 128 * 129 * @param new element to be added 130 */ 131 public synchronized void addFirst(CacheEntry element) { 132 if (elementCount == 0) { 133 firstElement = element; 134 lastElement = element; 135 element.prev = null; 136 element.next = null; 137 } else { 138 element.next = firstElement; 139 firstElement.prev = element; 140 element.prev = null; 141 firstElement = element; 142 } 143 elementCount++; 144 } 145 146 /** 147 * Removes the specified elemntent form the list. 148 * 149 * @param element 150 * to be removed 151 */ 152 public synchronized void removeEntry(CacheEntry element) { 153 if (element.next != null) { 154 element.next.prev = element.prev; 155 } 156 if (element.prev != null) { 157 element.prev.next = element.next; 158 } 159 if (element == firstElement) 160 firstElement = element.next; 161 if (element == lastElement) 162 lastElement = element.prev; 163 element.next = null; 164 element.prev = null; 165 elementCount--; 166 } 167 168 public synchronized void moveElementToFirstPos(CacheEntry entry) { 169 if (firstElement == entry) 170 return; 171 removeEntry(entry); 172 addFirst(entry); 173 } 174 175 public int getElementCount() { 176 return elementCount; 177 } 178 179 public CacheEntry getLastElement() { 180 return lastElement; 129 181 } 130 182 } -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/Tile.java
r9263 r9288 7 7 import java.awt.geom.AffineTransform; 8 8 import java.awt.image.BufferedImage; 9 import java.io.DataInputStream;10 import java.io.IOException;11 import java.net.URL;12 import java.net.URLConnection;13 14 import javax.imageio.ImageIO;15 9 16 10 /** … … 30 24 public static final int WIDTH = 256; 31 25 public static final int HEIGHT = 256; 32 public static final int WIDTH_HALF = 128;33 public static final int HEIGHT_HALF = 128;34 26 35 27 /** … … 63 55 Graphics2D g = (Graphics2D) tmpImage.getGraphics(); 64 56 // g.drawImage(image, 0, 0, null); 65 for (int zoomDiff = 1; zoomDiff < 3; zoomDiff++) {57 for (int zoomDiff = 1; zoomDiff < 5; zoomDiff++) { 66 58 // first we check if there are already the 2^x tiles 67 59 // of a higher detail level 68 60 int zoom_high = zoom + zoomDiff; 69 if (zoom _high <= JMapViewer.MAX_ZOOM) {61 if (zoomDiff < 3 && zoom_high <= JMapViewer.MAX_ZOOM) { 70 62 int factor = 1 << zoomDiff; 71 63 int xtile_high = xtile << zoomDiff; … … 135 127 } 136 128 129 public void setImage(BufferedImage image) { 130 this.image = image; 131 } 132 137 133 /** 138 134 * @return key that identifies a tile … … 146 142 } 147 143 148 public synchronized void loadTileImage() throws IOException { 149 if (loaded) 150 return; 151 URL url; 152 URLConnection urlConn; 153 DataInputStream input; 154 url = new URL("http://tile.openstreetmap.org/" + zoom + "/" + xtile + "/" + ytile + ".png"); 155 // System.out.println(url); 156 urlConn = url.openConnection(); 157 // urlConn.setUseCaches(false); 158 input = new DataInputStream(urlConn.getInputStream()); 159 image = ImageIO.read(input); 160 input.close(); 161 loaded = true; 144 public void setLoaded(boolean loaded) { 145 this.loaded = loaded; 162 146 } 163 147 … … 179 163 180 164 @Override 165 public String toString() { 166 return "Tile " + getTileKey(xtile, ytile, zoom); 167 } 168 169 @Override 181 170 public boolean equals(Object obj) { 182 171 if (!(obj instanceof Tile)) -
applications/viewer/jmapviewer/src/org/openstreetmap/gui/jmapviewer/TileCache.java
r9095 r9288 34 34 */ 35 35 public void addTile(Tile tile); 36 37 /** 38 * @return the number of tiles hold by the cache 39 */ 40 public int getTileCount(); 36 41 }
Note:
See TracChangeset
for help on using the changeset viewer.