package com.revolsys.swing.map.layer.raster; import java.awt.Graphics2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.revolsys.collection.map.MapEx; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.raster.GeoreferencedImage; import com.revolsys.swing.map.Viewport2D; import com.revolsys.swing.map.layer.AbstractLayerRenderer; import com.revolsys.swing.map.layer.AbstractTiledImageLayer; import com.revolsys.swing.map.layer.MapTile; import com.revolsys.swing.parallel.RunnableSwingWorkerManager; import com.revolsys.util.Cancellable; import com.revolsys.util.Property; public class TiledImageLayerRenderer extends AbstractLayerRenderer<AbstractTiledImageLayer> implements PropertyChangeListener { public static final String TILES_LOADED = "loading"; private static RunnableSwingWorkerManager tileLoaderManager = new RunnableSwingWorkerManager( "Load Map Tiles"); private final Map<MapTile, MapTile> cachedTiles = new HashMap<>(); private GeometryFactory geometryFactory; private final List<Runnable> loadingTasks = new ArrayList<>(); private double resolution; public TiledImageLayerRenderer(final AbstractTiledImageLayer layer) { super("tiledImage", layer); Property.addListener(layer, this); } @Override public void propertyChange(final PropertyChangeEvent event) { final Object newValue = event.getNewValue(); if (newValue instanceof BoundingBox) { final BoundingBox newBoundingBox = (BoundingBox)newValue; synchronized (this.cachedTiles) { final List<MapTile> mapTiles = new ArrayList<>(this.cachedTiles.keySet()); final GeometryFactory newGeometryFactory = newBoundingBox.getGeometryFactory(); for (final MapTile mapTile : mapTiles) { final BoundingBox boundingBox = mapTile.getBoundingBox(); final GeometryFactory geometryFactory = boundingBox.getGeometryFactory(); if (!geometryFactory.equals(newGeometryFactory) || !newBoundingBox.intersects(boundingBox)) { this.cachedTiles.remove(boundingBox); } } } } else if (!TILES_LOADED.equals(event.getPropertyName())) { synchronized (this.cachedTiles) { this.cachedTiles.clear(); // if (this.tileLoaderProcess != null) { // this.tileLoaderProcess.cancel(true); // } } } } @Override public void render(final Viewport2D viewport, final Cancellable cancellable, final AbstractTiledImageLayer layer) { final GeometryFactory geometryFactory = viewport.getGeometryFactory(); final double resolution = layer.getResolution(viewport); synchronized (this.cachedTiles) { if (resolution != this.resolution || geometryFactory != this.geometryFactory) { this.resolution = resolution; this.geometryFactory = geometryFactory; this.cachedTiles.clear(); tileLoaderManager.removeTasks(this.loadingTasks); this.loadingTasks.clear(); } } final List<Runnable> tasks = new ArrayList<>(); final List<MapTile> mapTiles = layer.getOverlappingMapTiles(viewport); for (final MapTile mapTile : cancellable.cancellable(mapTiles)) { if (mapTile != null) { MapTile cachedTile = null; synchronized (this.cachedTiles) { cachedTile = this.cachedTiles.get(mapTile); if (cachedTile == null) { cachedTile = mapTile; this.cachedTiles.put(cachedTile, cachedTile); final Runnable task = new TileLoadTask(this, cancellable, geometryFactory, cachedTile); tasks.add(task); } } if (!cancellable.isCancelled()) { final GeoreferencedImage image = cachedTile.getImage(geometryFactory); final Graphics2D graphics = viewport.getGraphics(); if (graphics != null) { GeoreferencedImageLayerRenderer.render(viewport, graphics, image, false); } } } } synchronized (this.loadingTasks) { this.loadingTasks.addAll(tasks); tileLoaderManager.addTasks(tasks); } } public void setLoaded(final TileLoadTask tileLoadTask) { this.loadingTasks.remove(tileLoadTask); final AbstractTiledImageLayer layer = getLayer(); if (layer != null) { layer.firePropertyChange(TILES_LOADED, false, true); } } @Override public MapEx toMap() { return MapEx.EMPTY; } }