All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jaitools.tilecache.DiskCachedTile Maven / Gradle / Ivy

Go to download

Provides a single jar containing all JAITools modules which you can use instead of including individual modules in your project. Note: It does not include the Jiffle scripting language or Jiffle image operator.

There is a newer version: 1.4.0
Show newest version
/* 
 *  Copyright (c) 2009-2011, Michael Bedward. All rights reserved. 
 *   
 *  Redistribution and use in source and binary forms, with or without modification, 
 *  are permitted provided that the following conditions are met: 
 *   
 *  - Redistributions of source code must retain the above copyright notice, this  
 *    list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice, this 
 *    list of conditions and the following disclaimer in the documentation and/or 
 *    other materials provided with the distribution.   
 *   
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */   

package org.jaitools.tilecache;

import java.awt.Point;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferDouble;
import java.awt.image.DataBufferFloat;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferShort;
import java.awt.image.DataBufferUShort;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.CachedTile;

/**
 * A managed tile class for {@code DiskMemTileCache}. Represents an image tile
 * that can be cached on disk and/or in memory.
 *
 * @see DiskMemTileCache
 *
 * @author Michael Bedward
 * @author Simone Giannecchini, GeoSolutions SAS
 * @since 1.0
 * @version $Id$
 */
public final class DiskCachedTile implements CachedTile {

    private static final Logger LOGGER = Logger.getLogger("org.jaitools.tilecache");

    /**
     * Constants identifying tile actions.
     */
    public enum TileAction {

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile has been added to the cache
         */
        ACTION_ADDED(0, "added to cache"),

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile has been added to the cache and immediately loaded
         * into memory
         */
        ACTION_ADDED_RESIDENT(1, "added to cache and placed into memory"),

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile becomes resident in memory
         */
        ACTION_RESIDENT(2, "placed into memory"),

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile is removed from memory
         */
        ACTION_NON_RESIDENT(3, "removed from memory"),

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile is removed from the cache entirely
         */
        ACTION_REMOVED(4, "removed from the cache"),

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile is accessed via the cache
         */
        ACTION_ACCESSED(5, "accessed"),

        /**
         * Value that will be returned by {@linkplain #getAction()} when
         * the tile's raster has been garbage collected
         */
        ACTION_GARBAGE_COLLECTED(6, "garbage collected");

        /**
         * Map for the reverse lookup facility
         */
        private static final Map lookup;
        static {
            lookup = new HashMap();
            for (TileAction t : EnumSet.allOf(TileAction.class)) {
                lookup.put(t.getAction(), t);
            }
        }
        /** An int associated to this action.*/
        private final int action;
        /** Description of the action. */
        private final String desc;

        /**
         * Private constructor to have maximum control over the values we use for this action.
         * @param action an int associated to this action for interoperability with {@link CachedTile} interface.
         */
        private TileAction(final int action, final String desc) {
            this.action = action;
            this.desc = desc;
        }

        /**
         * Retrieves an int associated to this action for interoperability with {@link CachedTile} interface.
         * @return an int associated to this action for interoperability with {@link CachedTile} interface.
         */
        public int getAction() {
            return action;
        }

        /**
         * Retrieves a description of this action
         * @return a description of this action
         */
        public String getDescription() {
            return desc;
        }

        /**
         * Reverse lookup.
         * @param actionValue integer value of the action
         * @return the corresponding TileAction
         */
        public static TileAction get(int actionValue) {
            return lookup.get(actionValue);
        }

        /**
         * The default action.
         * @return the default action.
         */
        public static TileAction getDefault() {
            return ACTION_ACCESSED;
        }
    }

    /**
     * The prefix used for temporary cache files
     * data
     */
    public static final String FILE_PREFIX = "tile";

    /**
     * The suffix used for temporary cache files
     */
    public static final String FILE_SUFFIX = ".tmp";

    /**
     * The folder used to store tiles that are being newly cached to disk.
     * If {@code null} (the default) the system's default folder is used.
     * It is safe (though not necessarily sensible) to change this property
     * after some tiles have already been cached to disk.
     */
    private static File cacheFolder = null;
    private static final Object folderLock = new Object();

    private final Object id;
    private final WeakReference ownerRef;
    private final int tileX;
    private final int tileY;
    private final Object tileCacheMetric;
    private long timeStamp;
    private final int  numBanks;
    private final int  dataLen;
    private final long memorySize;
    private File file;
    private final Point location;
    private final boolean isWritable;

    private TileAction action =TileAction.getDefault();

    /**
     * Get the current cache folder. This is the folder in which
     * newly created temporary files holding tile data will be created.
     *
     * @return a new {@code File} object for the current cache folder
     *         or {@code null} if the default system folder is being
     *         used (ie. the folder corresponding to the {@code java.io.file}
     *         System property
     */
    public static File getCacheFolder() {
        File file = null;
        if (cacheFolder != null) {
            file = new File(cacheFolder.toURI());
        }

        return file;
    }

    /**
     * Set the current cache folder. This is the folder in which
     * newly created temporary files holding tile data will be created.
     * 

* It is safe, though not necessarily sensible, to change the * cache folder while one or more caches are running. * * @param folder the new cache folder or {@code null} to use * the System's default temporary file folder */ public static void setCacheFolder(File folder) { synchronized(folderLock) { if (folder == null) { cacheFolder = null; } else { cacheFolder = new File(folder.toURI()); } } } /** * Package-private constructor * * @param id the tile's unique identifier, allocated by the cache * @param owner the image to which this tile belongs * @param tileX the column index for this tile in the image's tile grid * @param tileY the row index for this tile in the image's tile grid * @param raster the image data for this tile * @param writeToFile if true, the tile's data will be cached to disk * immediately; if false, disk caching is deferred * @param tileCacheMetric optional tile cache metric for use in scheduling * (may be {@code null}) * * @throws IOException if an attempt to write the tile to disk fails */ DiskCachedTile(Object id, RenderedImage owner, int tileX, int tileY, Raster raster, boolean writeToFile, Object tileCacheMetric) throws IOException { if (owner == null || raster == null) { throw new IllegalArgumentException( "All of owner, tile and file args must be non-null"); } this.id = id; this.ownerRef = new WeakReference(owner); this.tileX = tileX; this.tileY = tileY; this.tileCacheMetric = tileCacheMetric; this.location = raster.getBounds().getLocation(); this.isWritable = (raster instanceof WritableRaster); DataBuffer db = raster.getDataBuffer(); numBanks = db.getNumBanks(); dataLen = db.getSize(); memorySize = DataBuffer.getDataTypeSize(db.getDataType()) / 8L * dataLen * numBanks; if (writeToFile ) { writeData(raster); } setTileTimeStamp(System.currentTimeMillis()); } /** * Returns a string representation of this cached tile */ @Override public String toString() { RenderedImage o = getOwner(); String ostring = o == null ? "null" : o.toString(); Raster t = getTile(); String tstring = t == null ? "null" : t.toString(); return getClass().getName() + "@" + Integer.toHexString(hashCode()) + ": owner = " + ostring + " tileX = " + Integer.toString(tileX) + " tileY = " + Integer.toString(tileY) + " tile = " + tstring + " id = " + ((id instanceof Long)? Long.toHexString(((Long)id).longValue()) : id.toString()) + " memorySize = " + Long.toString(memorySize) + " timeStamp = " + Long.toString(timeStamp) + " file = " + file.getPath(); } /** * Do not use this method. *

* It is implemented to satisfy the CAchedTile interface but calling it will * provoke an UnsupportedOperationException. *

* To get a tile, use {@linkplain DiskBasedTileCache#getTile(java.awt.image.RenderedImage, int, int) } */ public Raster getTile() { throw new UnsupportedOperationException("Can't get a tile directly from a DiskCachedTile object"); } /** * Gets the image that owns this tile. * * @return the owning image */ public RenderedImage getOwner() { return ownerRef.get(); } /** * Gets the last time of access for this tile. * * @return the time stamp */ public long getTileTimeStamp() { return timeStamp; } /** * Gets the tile cache metric for this tile. * * @return the metric */ public Object getTileCacheMetric() { return tileCacheMetric; } /** * Gets the tile size in bytes. This is actually the * size of the raster associated with this cached tile instance. * * @return tile size in bytes */ public long getTileSize() { return memorySize; } /** * Gets the most recent action for this tile. More information can be * retrieved by getting the corresponding {@linkplain DiskCachedTile.TileAction} * constant as shown here: *


     * int code = tile.getAction();
     * TileAction action = TileAction.get(code);
     * System.out.println("tile action: " + action.getDescription());
     * 
* * @return the most recent action */ public int getAction() { return action.ordinal(); } /** * Queries if this tile has been cached to disk. This method is * a short-cut for: {@code getFile() != null}. * * @return {@code true} if the tile is cached on disk; {@code false} otherwise */ public boolean cachedToDisk() { return file != null; } /** * Gets this tile's disk cache file. Returns {@code null} if the tile has not * been cached to disk. * * @return the disk cache file for this tile or {@code null} */ public File getFile() { return file; } /** * Deletes this tile's disk cache file. If the file could not be deleted * a warning is logged. */ public void deleteDiskCopy() { if (file != null) { if (!file.delete()) { LOGGER.log(Level.WARNING, "Unable to delete cached image tile file: {0}", file.getPath()); } } } /** * Gets the unique ID for this tile. The ID is a combination of * either the JAI-generated unique ID of the owning image or the image's * hash key if a unique ID was not available plus the tile index. * The returned object will be either Long or BigInteger. * * @return tile ID */ public Object getTileId() { return id; } /** * Gets this tile's location in its parent image in pixel coordinates * * @return tile origin expressed in parent image coordinates */ public Point getLocation() { return new Point(location); } /** * Gets the X ordinate of this tile in the parent image tile grid. * * @return tile grid X ordinate */ public int getTileX() { return tileX; } /** * Gets the Y ordinate of this tile in the parent image tile grid. * * @return tile grid Y ordinate */ public int getTileY() { return tileY; } /** * Queries if this tile is writable. * * @return {@code true} if writable; {@code false} otherwise */ public boolean isWritable() { return isWritable; } /** * Package-private method called by the controlling {@linkplain DiskBasedTileCache} * object when the tile is added to, or removed from, the cache. */ void setAction( TileAction action ) { this.action = action; } /** * Package-private method called by the controlling {@linkplain DiskBasedTileCache} * object when the tile is accessed */ void setTileTimeStamp(long time) { this.timeStamp = time; } /** * Package-private method that reads data for the raster associated with this tile * from disk * * @return a new instance of Raster or WritableRaster */ Raster readData() { ImageInputStream strm = null; DataBuffer dataBuf = null; RenderedImage img = ownerRef.get(); Raster raster = null; if (file != null && img != null) { try { strm = ImageIO.createImageInputStream(file); switch (img.getSampleModel().getDataType()) { case DataBuffer.TYPE_BYTE: { byte[][] bankData = new byte[numBanks][dataLen]; for (int i = 0; i < numBanks; i++) { int numRead = strm.read(bankData[i], 0, dataLen); if (numRead < numBanks) { throw new RuntimeException( "Cached tile file appears to be truncated"); } } dataBuf = new DataBufferByte(bankData, dataLen); } break; case DataBuffer.TYPE_DOUBLE: { double[][] bankData = new double[numBanks][dataLen]; for (int i = 0; i < numBanks; i++) { strm.readFully(bankData[i], 0, dataLen); } dataBuf = new DataBufferDouble(bankData, dataLen); } break; case DataBuffer.TYPE_FLOAT: { float[][] bankData = new float[numBanks][dataLen]; for (int i = 0; i < numBanks; i++) { strm.readFully(bankData[i], 0, dataLen); } dataBuf = new DataBufferFloat(bankData, dataLen); } break; case DataBuffer.TYPE_INT: { int[][] bankData = new int[numBanks][dataLen]; for (int i = 0; i < numBanks; i++) { strm.readFully(bankData[i], 0, dataLen); } dataBuf = new DataBufferInt(bankData, dataLen); } break; case DataBuffer.TYPE_SHORT: { short[][] bankData = new short[numBanks][dataLen]; for (int i = 0; i < numBanks; i++) { strm.readFully(bankData[i], 0, dataLen); } dataBuf = new DataBufferShort(bankData, dataLen); } break; case DataBuffer.TYPE_USHORT: { short[][] bankData = new short[numBanks][dataLen]; for (int i = 0; i < numBanks; i++) { strm.readFully(bankData[i], 0, dataLen); } dataBuf = new DataBufferUShort(bankData, dataLen); } break; default: throw new UnsupportedOperationException("Unsupported image data type"); } } catch (FileNotFoundException ex) { LOGGER.log(Level.SEVERE, "Failed to read image tile data", ex); return null; } catch (IOException ex) { LOGGER.log(Level.SEVERE, "Failed to read image tile data", ex); return null; } finally{ if(strm!=null) try{ strm.close(); }catch (Throwable e) { // chew me } } } if (dataBuf != null) { if (isWritable) { raster = Raster.createWritableRaster(img.getSampleModel(), dataBuf, location); } else { raster = Raster.createRaster(img.getSampleModel(), dataBuf, location); } } return raster; } /** * Write data for the raster associated with this tile to * disk. This may be called by DiskMemTileCache * as well as be the tile itself. */ void writeData(Raster raster) throws IOException { ImageOutputStream strm = null; DataBuffer dataBuf = raster.getDataBuffer(); if (file == null) { // first time this tile has been written to disk file = createFile(); } try { strm = ImageIO.createImageOutputStream(file); switch (dataBuf.getDataType()) { case DataBuffer.TYPE_BYTE: { byte[] bankData; for (int i = 0; i < numBanks; i++) { bankData = ((DataBufferByte) dataBuf).getData(i); strm.write(bankData); } } break; case DataBuffer.TYPE_DOUBLE: { double[] bankData; for (int i = 0; i < numBanks; i++) { bankData = ((DataBufferDouble) dataBuf).getData(i); strm.writeDoubles(bankData, 0, dataLen); } } break; case DataBuffer.TYPE_FLOAT: { float[] bankData; for (int i = 0; i < numBanks; i++) { bankData = ((DataBufferFloat) dataBuf).getData(i); strm.writeFloats(bankData, 0, dataLen); } } break; case DataBuffer.TYPE_INT: { int[] bankData; for (int i = 0; i < numBanks; i++) { bankData = ((DataBufferInt) dataBuf).getData(i); strm.writeInts(bankData, 0, dataLen); } } break; case DataBuffer.TYPE_SHORT: { short[] bankData; for (int i = 0; i < numBanks; i++) { bankData = ((DataBufferShort) dataBuf).getData(i); strm.writeShorts(bankData, 0, dataLen); } } break; case DataBuffer.TYPE_USHORT: { short[] bankData; for (int i = 0; i < numBanks; i++) { bankData = ((DataBufferUShort) dataBuf).getData(i); strm.writeShorts(bankData, 0, dataLen); } } break; default: throw new UnsupportedOperationException("Unsupported image data type"); } } catch (FileNotFoundException ex) { LOGGER.log(Level.SEVERE, "Failed to write image tile data", ex); } catch (IOException ex) { LOGGER.log(Level.SEVERE, "Failed to write image tile data", ex); } finally{ if(strm!=null) try{ strm.close(); }catch (Throwable e) { // chew me } } } /** * Create a file to cache the given tile on disk. Presently * this method does nothing more than delegate to File.createTempFile * * @throws java.io.IOException */ private File createFile() throws IOException { synchronized(folderLock) { return File.createTempFile(FILE_PREFIX, FILE_SUFFIX, cacheFolder); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy