19 package org.sleuthkit.autopsy.geolocation;
21 import java.awt.image.BufferedImage;
22 import java.io.ByteArrayInputStream;
23 import java.io.IOException;
24 import java.lang.reflect.InvocationTargetException;
26 import java.net.URISyntaxException;
27 import java.util.Comparator;
28 import java.util.HashMap;
30 import java.util.concurrent.BlockingQueue;
31 import java.util.concurrent.ExecutorService;
32 import java.util.concurrent.Executors;
33 import java.util.concurrent.PriorityBlockingQueue;
34 import java.util.concurrent.ThreadFactory;
35 import java.util.logging.Level;
37 import javax.imageio.ImageIO;
38 import javax.swing.SwingUtilities;
40 import org.jxmapviewer.viewer.Tile;
41 import org.jxmapviewer.viewer.TileCache;
42 import org.jxmapviewer.viewer.TileFactory;
43 import org.jxmapviewer.viewer.util.GeoUtil;
54 final class MBTilesTileFactory
extends TileFactory {
56 private static final Logger logger = Logger.getLogger(MBTilesTileFactory.class.getName());
58 private volatile int pendingTiles = 0;
59 private static final int THREAD_POOL_SIZE = 4;
60 private ExecutorService service;
62 private final Map<String, Tile> tileMap;
64 private final TileCache cache;
66 private MBTilesFileConnector connector;
75 MBTilesTileFactory(String filePath)
throws GeoLocationDataException {
76 this(
new MBTilesFileConnector(filePath));
84 private MBTilesTileFactory(MBTilesFileConnector connector) {
85 super(connector.getInfo());
86 this.connector = connector;
87 cache =
new TileCache();
88 tileMap =
new HashMap<>();
102 public Tile getTile(
int x,
int y,
int zoom) {
103 return getTile(x, y, zoom,
true);
116 private Tile getTile(
int tpx,
int tpy,
int zoom,
boolean eagerLoad) {
120 int numTilesWide = (int) getMapSize(zoom).getWidth();
122 tileX = numTilesWide - (Math.abs(tileX) % numTilesWide);
125 tileX %= numTilesWide;
128 String url = getInfo().getTileUrl(tileX, tileY, zoom);
130 Tile.Priority pri = Tile.Priority.High;
132 pri = Tile.Priority.Low;
135 if (!tileMap.containsKey(url)) {
137 if (!GeoUtil.isValidTile(tileX, tileY, zoom, getInfo())) {
138 tile =
new MBTilesTile(tileX, tileY, zoom);
140 tile =
new MBTilesTile(tileX, tileY, zoom, url, pri);
143 tileMap.put(url, tile);
145 tile = tileMap.get(url);
148 if (tile.getPriority() == Tile.Priority.Low && eagerLoad && !tile.isLoaded()) {
161 TileCache getTileCache() {
171 private final BlockingQueue<Tile> tileQueue =
new PriorityBlockingQueue<>(5,
new Comparator<Tile>() {
173 public int compare(Tile o1, Tile o2) {
174 if (o1.getPriority() == Tile.Priority.Low && o2.getPriority() == Tile.Priority.High) {
177 if (o1.getPriority() == Tile.Priority.High && o2.getPriority() == Tile.Priority.Low) {
192 synchronized ExecutorService getService() {
193 if (service == null) {
195 service = Executors.newFixedThreadPool(THREAD_POOL_SIZE,
new ThreadFactory() {
196 private int count = 0;
199 public Thread newThread(Runnable r) {
200 Thread t =
new Thread(r,
"tile-pool-" + count++);
201 t.setPriority(Thread.MIN_PRIORITY);
211 public void dispose() {
212 if (service != null) {
219 protected synchronized void startLoading(Tile tile) {
220 if (tile.isLoading()) {
224 tile.setLoading(
true);
227 getService().submit(
new TileRunner());
228 }
catch (InterruptedException ex) {
238 synchronized void promote(Tile tile) {
239 if (tileQueue.contains(tile)) {
241 tileQueue.remove(tile);
242 tile.setPriority(Tile.Priority.High);
244 }
catch (InterruptedException ex) {
253 int getPendingTiles() {
272 protected URI
getURI(Tile tile)
throws URISyntaxException {
273 if (tile.getURL() == null) {
276 return new URI(tile.getURL());
287 final Tile tile = tileQueue.remove();
289 int remainingAttempts = 3;
290 while (!tile.isLoaded() && remainingAttempts > 0) {
294 BufferedImage img = cache.get(uri);
299 addImageToTile(tile, img);
301 }
catch (OutOfMemoryError memErr) {
302 cache.needMoreMemory();
303 }
catch (IOException |
GeoLocationDataException | InterruptedException | InvocationTargetException | URISyntaxException ex) {
304 if (remainingAttempts == 0) {
305 logger.log(Level.SEVERE, String.format(
"Failed to load a tile at URL: %s, stopping", tile.getURL()), ex);
307 logger.log(Level.WARNING,
"Failed to load a tile at URL: " + tile.getURL() +
", retrying", ex);
311 tile.setLoading(
false);
323 BufferedImage img = null;
324 byte[] bimg = connector.getTileBytes(uri.toString());
325 if (bimg != null && bimg.length > 0) {
326 img = ImageIO.read(
new ByteArrayInputStream(bimg));
327 cache.put(uri, bimg, img);
339 private void addImageToTile(Tile tile, BufferedImage image)
throws InterruptedException, InvocationTargetException {
340 SwingUtilities.invokeAndWait(
new Runnable() {
343 if (tile instanceof MBTilesTile) {
344 ((MBTilesTile) tile).setImage(image);
347 fireTileLoadedEvent(tile);