/* * Tile.java * * Created on March 14, 2006, 4:53 PM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package org.jdesktop.swingx.mapviewer; import java.awt.EventQueue; import java.awt.image.BufferedImage; import java.beans.PropertyChangeListener; import java.lang.ref.SoftReference; import java.net.URI; import java.util.Random; import javax.swing.SwingUtilities; import org.jdesktop.beans.AbstractBean; /** * The Tile class represents a particular square image piece of the world bitmap * at a particular zoom level. * * @author joshy * @author Simon Templer * * @version $Id$ */ public class Tile extends AbstractBean implements TileInfo { /** * Priority enumeration */ public enum Priority { /** High priority */ High, /** Low priority */ Low } private Priority priority = Priority.High; // private static final Log log = LogFactory.getLog(Tile.class); private boolean isLoading = false; private TileFactory tileFactory; /** * If an error occurs while loading a tile, store the exception here. */ private Throwable error; /** * The url of the image to load for this tile and its alternatives */ private URI[] uris; /** * The index of the URI in {@link #uris} that should be returned when * {@link #getURI()} is called. Will be increased every time, * {@link #notifyBeforeRetry()} is called. */ private int currentURI = 0; /** * Indicates that loading has succeeded. A PropertyChangeEvent will be fired * when the loading is completed */ private boolean loaded = false; /** * The tile coordinates and zoom level */ private final int zoom, x, y; /** * The image loaded for this Tile */ SoftReference<BufferedImage> image = new SoftReference<BufferedImage>(null); /** * Create a new Tile at the specified tile point and zoom level * * @param x the x coordinate * @param y the y coordinate * @param zoom the zoom level */ public Tile(int x, int y, int zoom) { loaded = false; this.zoom = zoom; this.x = x; this.y = y; } /** * Create a new Tile that loads its data from the given URI. The URI must * resolve to an image * * @param x the tile x ordinate * @param y the tile y ordinate * @param zoom the tile's zoom level * @param uris the tile image URI (and its alternatives) * @param priority the priority * @param tileFactory the tile factory */ Tile(int x, int y, int zoom, Priority priority, TileFactory tileFactory, URI... uris) { this.uris = uris; if (uris != null) { currentURI = new Random().nextInt(uris.length); } loaded = false; this.zoom = zoom; this.x = x; this.y = y; this.priority = priority; this.tileFactory = tileFactory; // startLoading(); } /** * * Indicates if this tile's underlying image has been successfully loaded * yet. * * @return true if the Tile has been loaded */ public synchronized boolean isLoaded() { return loaded; } /** * Toggles the loaded state, and fires the appropriate property change * notification * * @param loaded the loaded state to set */ synchronized void setLoaded(boolean loaded) { boolean old = isLoaded(); this.loaded = loaded; firePropertyChange("loaded", old, isLoaded()); } /** * Returns the last error in a possible chain of errors that occurred during * the loading of the tile * * @return the last error that occurred while loading the tile */ public Throwable getUnrecoverableError() { return error; } /** * Returns the {@link Throwable} tied to any error that may have ocurred * while loading the tile. This error may change several times if multiple * errors occur * * @return the error that occurred while loading the tile */ public Throwable getLoadingError() { return error; } /** * Returns the Image associated with this Tile. This is a read only property * This may return null at any time, however if this returns null, a load * operation will automatically be started for it. * * @return the tile image */ public BufferedImage getImage() { BufferedImage img = image.get(); if (img == null) { setLoaded(false); tileFactory.startLoading(this); } return img; } /** * @see org.jdesktop.swingx.mapviewer.TileInfo#getZoom() */ @Override public int getZoom() { return zoom; } ////////////////// JavaOne Hack/////////////////// private PropertyChangeListener uniqueListener = null; /** * Adds a single property change listener. If a listener has been previously * added then it will be replaced by the new one. * * @param propertyName * @param listener */ @SuppressWarnings("javadoc") public void addUniquePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (uniqueListener != null && uniqueListener != listener) { removePropertyChangeListener(propertyName, uniqueListener); } if (uniqueListener != listener) { uniqueListener = listener; addPropertyChangeListener(propertyName, uniqueListener); } } ///////////////// End JavaOne Hack///////////////// /** * Fire a property change on the event dispatch thread * * @param propertyName the property name * @param oldValue the old property value * @param newValue the new property value */ void firePropertyChangeOnEDT(final String propertyName, final Object oldValue, final Object newValue) { if (!EventQueue.isDispatchThread()) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { firePropertyChange(propertyName, oldValue, newValue); } }); } } /** * @return the error */ public Throwable getError() { return error; } /** * @param error the error to set */ public void setError(Throwable error) { this.error = error; } /** * @return the isLoading */ public boolean isLoading() { return isLoading; } /** * @param isLoading the isLoading to set */ public void setLoading(boolean isLoading) { this.isLoading = isLoading; } /** * Gets the loading priority of this tile. * * @return the tile's priority */ public Priority getPriority() { return priority; } /** * Set the loading priority of this tile. * * @param priority the priority to set */ public void setPriority(Priority priority) { this.priority = priority; } /** * @see org.jdesktop.swingx.mapviewer.TileInfo#getURI() */ @Override public URI getURI() { if (uris == null) { return null; } return uris[currentURI]; } @Override public URI getIdentifier() { if (uris == null) { return null; } return uris[0]; } /** * @see org.jdesktop.swingx.mapviewer.TileInfo#getX() */ @Override public int getX() { return x; } /** * @see org.jdesktop.swingx.mapviewer.TileInfo#getY() */ @Override public int getY() { return y; } /** * Notifies the tile that the tile runner is about to retry loading it. */ public void notifyBeforeRetry() { if (uris != null) { currentURI = (currentURI + 1) % uris.length; } } }