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

gov.nasa.worldwind.layers.rpf.RPFTiledImageLayer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.layers.rpf;

import com.jogamp.opengl.util.texture.*;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.cache.FileStore;
import gov.nasa.worldwind.formats.dds.DDSCompressor;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.layers.*;
import gov.nasa.worldwind.render.DrawContext;
import gov.nasa.worldwind.retrieve.*;
import gov.nasa.worldwind.util.*;

import java.awt.image.*;
import java.io.*;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * @author dcollins
 * @version $Id: RPFTiledImageLayer.java 1171 2013-02-11 21:45:02Z dcollins $
 */
public class RPFTiledImageLayer extends TiledImageLayer
{
    private AVList creationParams;
    private final RPFGenerator rpfGenerator;
    private final Object fileLock = new Object();

    public static final String RPF_ROOT_PATH = "rpf.RootPath";
    public static final String RPF_DATA_SERIES_ID = "rpf.DataSeriesId";

    static Collection createTopLevelTiles(AVList params)
    {
        if (params == null)
        {
            String message = Logging.getMessage("nullValue.LayerConfigParams");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        LevelSet levels = new LevelSet(initParams(params));
        Sector sector = levels.getSector();

        Level level = levels.getFirstLevel();
        Angle dLat = level.getTileDelta().getLatitude();
        Angle dLon = level.getTileDelta().getLongitude();
        Angle latOrigin = levels.getTileOrigin().getLatitude();
        Angle lonOrigin = levels.getTileOrigin().getLongitude();

        // Determine the row and column offset from the common World Wind global tiling origin.
        int firstRow = Tile.computeRow(dLat, sector.getMinLatitude(), latOrigin);
        int firstCol = Tile.computeColumn(dLon, sector.getMinLongitude(), lonOrigin);
        int lastRow = Tile.computeRow(dLat, sector.getMaxLatitude(), latOrigin);
        int lastCol = Tile.computeColumn(dLon, sector.getMaxLongitude(), lonOrigin);

        int nLatTiles = lastRow - firstRow + 1;
        int nLonTiles = lastCol - firstCol + 1;

        ArrayList topLevels = new ArrayList(nLatTiles * nLonTiles);

        Angle p1 = Tile.computeRowLatitude(firstRow, dLat, latOrigin);
        for (int row = firstRow; row <= lastRow; row++)
        {
            Angle p2;
            p2 = p1.add(dLat);

            Angle t1 = Tile.computeColumnLongitude(firstCol, dLon, lonOrigin);
            for (int col = firstCol; col <= lastCol; col++)
            {
                Angle t2;
                t2 = t1.add(dLon);

                topLevels.add(new Tile(new Sector(p1, p2, t1, t2), level, row, col));
                t1 = t2;
            }
            p1 = p2;
        }

        return topLevels;
    }

    static String getFileIndexCachePath(String rootPath, String dataSeriesId)
    {
        String path = null;
        if (rootPath != null && dataSeriesId != null)
        {
            path = WWIO.formPath(
                rootPath,
                dataSeriesId,
                "rpf_file_index.idx");
        }
        return path;
    }

    public static RPFTiledImageLayer fromRestorableState(String stateInXml)
    {
        if (stateInXml == null)
        {
            String message = Logging.getMessage("nullValue.StringIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        return new RPFTiledImageLayer(stateInXml);
    }

    public RPFTiledImageLayer(String stateInXml)
    {
        this(xmlStateToParams(stateInXml));

        RestorableSupport rs;
        try
        {
            rs = RestorableSupport.parse(stateInXml);
        }
        catch (Exception e)
        {
            // Parsing the document specified by stateInXml failed.
            String message = Logging.getMessage("generic.ExceptionAttemptingToParseStateXml", stateInXml);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message, e);
        }

        Boolean b = rs.getStateValueAsBoolean("rpf.LayerEnabled");
        if (b != null)
            this.setEnabled(b);

        Double d = rs.getStateValueAsDouble("rpf.Opacity");
        if (d != null)
            this.setOpacity(d);

        d = rs.getStateValueAsDouble("rpf.MinActiveAltitude");
        if (d != null)
            this.setMinActiveAltitude(d);

        d = rs.getStateValueAsDouble("rpf.MaxActiveAltitude");
        if (d != null)
            this.setMaxActiveAltitude(d);

        String s = rs.getStateValueAsString("rpf.LayerName");
        if (s != null)
            this.setName(s);

        b = rs.getStateValueAsBoolean("rpf.UseMipMaps");
        if (b != null)
            this.setUseMipMaps(b);

        b = rs.getStateValueAsBoolean("rpf.UseTransparentTextures");
        if (b != null)
            this.setUseTransparentTextures(b);

        RestorableSupport.StateObject so = rs.getStateObject("avlist");
        if (so != null)
        {
            RestorableSupport.StateObject[] avpairs = rs.getAllStateObjects(so, "");
            if (avpairs != null)
            {
                for (RestorableSupport.StateObject avp : avpairs)
                {
                    if (avp != null)
                        setValue(avp.getName(), avp.getValue());
                }
            }
        }
    }

    public RPFTiledImageLayer(AVList params)
    {
        super(new LevelSet(initParams(params)));

        this.initRPFFileIndex(params);
        this.creationParams = params.copy();
        this.rpfGenerator = new RPFGenerator(params);

        this.setValue(AVKey.CONSTRUCTION_PARAMETERS, params);
        //this.setUseMipMaps(true);
        this.setUseTransparentTextures(true);
        this.setName(makeTitle(params));
    }

    protected void initRPFFileIndex(AVList params)
    {
        // Load the RPFFileIndex associated with this RPFTiledImageLayer, and update the layer's expiry time according
        // to the last modified time on the RPFFileIndex.

        FileStore fileStore = WorldWind.getDataFileStore();

        // Root path and data series ID parameters should have already been validated in initParams().
        String rootPath = params.getStringValue(RPF_ROOT_PATH);
        String dataSeriesId = params.getStringValue(RPF_DATA_SERIES_ID);
        File file = fileStore.newFile(getFileIndexCachePath(rootPath, dataSeriesId));

        RPFFileIndex fileIndex = (RPFFileIndex) params.getValue(RPFGenerator.RPF_FILE_INDEX);
        if (fileIndex == null)
        {
            fileIndex = initFileIndex(file);
            if (fileIndex == null)
            {
                String message = Logging.getMessage("nullValue.RPFFileIndexIsNull");
                Logging.logger().severe(message);
                throw new IllegalArgumentException(message);
            }
            params.setValue(RPFGenerator.RPF_FILE_INDEX, fileIndex);
        }

        // Default to expiring data on the date the DDS converter was updated. If the RPFFileIndex's last-modified time
        // is newer than the default expiry time, then use newer of the two. This ensures that layer imagery always
        // reflects whats in the RPFFileIndex. If the layer has been re-imported (data has been added, or data has been
        // removed), then all previously created layer imagery will be expired (but not necessarily the preprocessed
        // data).
        Long expiryTime = new GregorianCalendar(2009, Calendar.FEBRUARY, 25).getTimeInMillis();
        if (file != null && file.lastModified() > expiryTime)
        {
            expiryTime = file.lastModified();
        }
        this.setExpiryTime(expiryTime);
    }

    private static AVList initParams(AVList params)
    {
        if (params == null)
        {
            String message = Logging.getMessage("nullValue.LayerConfigParams");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        String rootPath = params.getStringValue(RPF_ROOT_PATH);
        if (rootPath == null)
        {
            String message = Logging.getMessage("nullValue.RPFRootPath");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        String dataSeriesId = params.getStringValue(RPF_DATA_SERIES_ID);
        if (dataSeriesId == null)
        {
            String message = Logging.getMessage("nullValue.RPFDataSeriesIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        // Use a dummy value for service.
        if (params.getValue(AVKey.SERVICE) == null)
            params.setValue(AVKey.SERVICE, "file://" + RPFGenerator.class.getName() + "?");

        // Use a dummy value for dataset-name.
        if (params.getValue(AVKey.DATASET_NAME) == null)
            params.setValue(AVKey.DATASET_NAME, dataSeriesId);

        if (params.getValue(AVKey.LEVEL_ZERO_TILE_DELTA) == null)
        {
            Angle delta = Angle.fromDegrees(36);
            params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(delta, delta));
        }

        if (params.getValue(AVKey.TILE_WIDTH) == null)
            params.setValue(AVKey.TILE_WIDTH, 512);
        if (params.getValue(AVKey.TILE_HEIGHT) == null)
            params.setValue(AVKey.TILE_HEIGHT, 512);
        if (params.getValue(AVKey.FORMAT_SUFFIX) == null)
            params.setValue(AVKey.FORMAT_SUFFIX, ".dds");
        if (params.getValue(AVKey.NUM_LEVELS) == null)
            params.setValue(AVKey.NUM_LEVELS, 14); // approximately 0.5 meters per pixel
        if (params.getValue(AVKey.NUM_EMPTY_LEVELS) == null)
            params.setValue(AVKey.NUM_EMPTY_LEVELS, 0);

        params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder());

        // RPFTiledImageLayer is typically constructed either by the {@link RPFTiledImageProcessor}, or from restorable
        // state XML. In the first case, either the sector parameter or the RPFFileIndex parameter are specified by the
        // processor. In the latter case, the sector is restored as part of the state xml.
        Sector sector = (Sector) params.getValue(AVKey.SECTOR);
        if (sector == null)
        {
            RPFFileIndex fileIndex = (RPFFileIndex) params.getValue(RPFGenerator.RPF_FILE_INDEX);
            if (fileIndex != null && fileIndex.getIndexProperties() != null)
                sector = fileIndex.getIndexProperties().getBoundingSector();

            if (sector == null)
            {
                String message = Logging.getMessage("RPFTiledImageLayer.NoGeographicBoundingBox");
                Logging.logger().severe(message);
                throw new IllegalArgumentException(message);
            }

            params.setValue(AVKey.SECTOR, sector);
        }

        if (params.getValue(AVKey.DATA_CACHE_NAME) == null)
        {
            String cacheName = WWIO.formPath(rootPath, dataSeriesId);
            params.setValue(AVKey.DATA_CACHE_NAME, cacheName);
        }

        return params;
    }

    private static RPFFileIndex initFileIndex(File file)
    {
        ByteBuffer buffer;
        try
        {
            buffer = WWIO.mapFile(file);
        }
        catch (Exception e)
        {
            String message = "Exception while attempting to map file: " + file;
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
            buffer = null;
        }

        RPFFileIndex fileIndex = null;
        try
        {
            if (buffer != null)
            {
                fileIndex = new RPFFileIndex();
                fileIndex.load(buffer);
            }
        }
        catch (Exception e)
        {
            String message = "Exception while attempting to load RPFFileIndex: " + file;
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
            fileIndex = null;
        }

        return fileIndex;
    }

    private static String makeTitle(AVList params)
    {
        StringBuilder sb = new StringBuilder();

        Object o = params.getValue(RPFGenerator.RPF_FILE_INDEX);
        if (o != null && o instanceof RPFFileIndex)
        {
            RPFFileIndex fileIndex = (RPFFileIndex) o;
            if (fileIndex.getIndexProperties() != null)
            {
                if (fileIndex.getIndexProperties().getDescription() != null)
                    sb.append(fileIndex.getIndexProperties().getDescription());
                else
                    sb.append(fileIndex.getIndexProperties().getDataSeriesIdentifier());
            }
        }

        if (sb.length() == 0)
        {
            String rootPath = params.getStringValue(RPF_ROOT_PATH);
            String dataSeriesId = params.getStringValue(RPF_DATA_SERIES_ID);
            if (rootPath != null && dataSeriesId != null)
            {
                sb.append(rootPath).append(":").append(dataSeriesId);
            }
        }

        return sb.toString();
    }

    protected void checkResources()
    {
        // Intentionally left blank.
    }

    private RestorableSupport makeRestorableState(AVList params)
    {
        RestorableSupport rs = RestorableSupport.newRestorableSupport();
        // Creating a new RestorableSupport failed. RestorableSupport logged the problem, so just return null.
        if (rs == null)
            return null;

        for (Map.Entry p : params.getEntries())
        {
            if (p.getValue() instanceof LatLon)
            {
                rs.addStateValueAsDouble(p.getKey() + ".Latitude", ((LatLon) p.getValue()).getLatitude().degrees);
                rs.addStateValueAsDouble(p.getKey() + ".Longitude", ((LatLon) p.getValue()).getLongitude().degrees);
            }
            else if (p.getValue() instanceof Sector)
            {
                rs.addStateValueAsDouble(p.getKey() + ".MinLatitude", ((Sector) p.getValue()).getMinLatitude().degrees);
                rs.addStateValueAsDouble(p.getKey() + ".MaxLatitude", ((Sector) p.getValue()).getMaxLatitude().degrees);
                rs.addStateValueAsDouble(p.getKey() + ".MinLongitude",
                    ((Sector) p.getValue()).getMinLongitude().degrees);
                rs.addStateValueAsDouble(p.getKey() + ".MaxLongitude",
                    ((Sector) p.getValue()).getMaxLongitude().degrees);
            }
            else if (p.getValue() instanceof URLBuilder)
            {
                // Intentionally left blank. URLBuilder will be created from scratch in fromRestorableState().
            }
            else if (p.getKey().equals(RPFGenerator.RPF_FILE_INDEX))
            {
                // Intentionally left blank.
            }
            else
            {
                super.getRestorableStateForAVPair(p.getKey(), p.getValue(), rs, null);
            }
        }

        rs.addStateValueAsBoolean("rpf.LayerEnabled", this.isEnabled());
        rs.addStateValueAsDouble("rpf.Opacity", this.getOpacity());
        rs.addStateValueAsDouble("rpf.MinActiveAltitude", this.getMinActiveAltitude());
        rs.addStateValueAsDouble("rpf.MaxActiveAltitude", this.getMaxActiveAltitude());
        rs.addStateValueAsString("rpf.LayerName", this.getName());
        rs.addStateValueAsBoolean("rpf.UseMipMaps", this.isUseMipMaps());
        rs.addStateValueAsBoolean("rpf.UseTransparentTextures", this.isUseTransparentTextures());

        RestorableSupport.StateObject so = rs.addStateObject("avlist");
        for (Map.Entry p : this.getEntries())
        {
            if (p.getKey().equals(AVKey.CONSTRUCTION_PARAMETERS))
                continue;

            super.getRestorableStateForAVPair(p.getKey(), p.getValue(), rs, so);
        }

        return rs;
    }

    public String getRestorableState()
    {
        return this.makeRestorableState(this.creationParams).getStateAsXml();
    }

    public static AVList xmlStateToParams(String stateInXml)
    {
        if (stateInXml == null)
        {
            String message = Logging.getMessage("nullValue.StringIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        RestorableSupport rs;
        try
        {
            rs = RestorableSupport.parse(stateInXml);
        }
        catch (Exception e)
        {
            // Parsing the document specified by stateInXml failed.
            String message = Logging.getMessage("generic.ExceptionAttemptingToParseStateXml", stateInXml);
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message, e);
        }

        AVList params = new AVListImpl();

        String s = rs.getStateValueAsString(RPF_ROOT_PATH);
        if (s != null)
            params.setValue(RPF_ROOT_PATH, s);

        s = rs.getStateValueAsString(RPF_DATA_SERIES_ID);
        if (s != null)
            params.setValue(RPF_DATA_SERIES_ID, s);

        s = rs.getStateValueAsString(AVKey.IMAGE_FORMAT);
        if (s != null)
            params.setValue(AVKey.IMAGE_FORMAT, s);

        s = rs.getStateValueAsString(AVKey.DATA_CACHE_NAME);
        if (s != null)
            params.setValue(AVKey.DATA_CACHE_NAME, s);

        s = rs.getStateValueAsString(AVKey.SERVICE);
        if (s != null)
            params.setValue(AVKey.SERVICE, s);

        s = rs.getStateValueAsString(AVKey.TITLE);
        if (s != null)
            params.setValue(AVKey.TITLE, s);

        s = rs.getStateValueAsString(AVKey.DISPLAY_NAME);
        if (s != null)
            params.setValue(AVKey.DISPLAY_NAME, s);

        RestorableSupport.adjustTitleAndDisplayName(params);

        s = rs.getStateValueAsString(AVKey.DATASET_NAME);
        if (s != null)
            params.setValue(AVKey.DATASET_NAME, s);

        s = rs.getStateValueAsString(AVKey.FORMAT_SUFFIX);
        if (s != null)
            params.setValue(AVKey.FORMAT_SUFFIX, s);

        s = rs.getStateValueAsString(AVKey.LAYER_NAMES);
        if (s != null)
            params.setValue(AVKey.LAYER_NAMES, s);

        s = rs.getStateValueAsString(AVKey.STYLE_NAMES);
        if (s != null)
            params.setValue(AVKey.STYLE_NAMES, s);

        Integer i = rs.getStateValueAsInteger(AVKey.NUM_EMPTY_LEVELS);
        if (i != null)
            params.setValue(AVKey.NUM_EMPTY_LEVELS, i);

        i = rs.getStateValueAsInteger(AVKey.NUM_LEVELS);
        if (i != null)
            params.setValue(AVKey.NUM_LEVELS, i);

        i = rs.getStateValueAsInteger(AVKey.TILE_WIDTH);
        if (i != null)
            params.setValue(AVKey.TILE_WIDTH, i);

        i = rs.getStateValueAsInteger(AVKey.TILE_HEIGHT);
        if (i != null)
            params.setValue(AVKey.TILE_HEIGHT, i);

        Double lat = rs.getStateValueAsDouble(AVKey.LEVEL_ZERO_TILE_DELTA + ".Latitude");
        Double lon = rs.getStateValueAsDouble(AVKey.LEVEL_ZERO_TILE_DELTA + ".Longitude");
        if (lat != null && lon != null)
            params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, LatLon.fromDegrees(lat, lon));

        Double minLat = rs.getStateValueAsDouble(AVKey.SECTOR + ".MinLatitude");
        Double minLon = rs.getStateValueAsDouble(AVKey.SECTOR + ".MinLongitude");
        Double maxLat = rs.getStateValueAsDouble(AVKey.SECTOR + ".MaxLatitude");
        Double maxLon = rs.getStateValueAsDouble(AVKey.SECTOR + ".MaxLongitude");
        if (minLat != null && minLon != null && maxLat != null && maxLon != null)
            params.setValue(AVKey.SECTOR, Sector.fromDegrees(minLat, maxLat, minLon, maxLon));

        params.setValue(AVKey.TILE_URL_BUILDER, new URLBuilder());

        return params;
    }

    public void restoreState(String stateInXml)
    {
        String message = Logging.getMessage("RestorableSupport.RestoreRequiresConstructor");
        Logging.logger().severe(message);
        throw new UnsupportedOperationException(message);
    }

    private static class URLBuilder implements TileUrlBuilder
    {
        public String URLTemplate = null;

        private URLBuilder()
        {
        }

        public java.net.URL getURL(Tile tile, String imageFormat) throws java.net.MalformedURLException
        {
            StringBuffer sb;
            if (this.URLTemplate == null)
            {
                sb = new StringBuffer(tile.getLevel().getService());
                sb.append("dataset=");
                sb.append(tile.getLevel().getDataset());
                sb.append("&width=");
                sb.append(tile.getLevel().getTileWidth());
                sb.append("&height=");
                sb.append(tile.getLevel().getTileHeight());

                this.URLTemplate = sb.toString();
            }
            else
            {
                sb = new StringBuffer(this.URLTemplate);
            }

            Sector s = tile.getSector();
            sb.append("&bbox=");
            sb.append(s.getMinLongitude().getDegrees());
            sb.append(",");
            sb.append(s.getMinLatitude().getDegrees());
            sb.append(",");
            sb.append(s.getMaxLongitude().getDegrees());
            sb.append(",");
            sb.append(s.getMaxLatitude().getDegrees());
            sb.append("&"); // terminate the query string

            return new java.net.URL(sb.toString().replace(" ", "%20"));
        }
    }

    protected void forceTextureLoad(TextureTile tile)
    {
        final java.net.URL textureURL = WorldWind.getDataFileStore().findFile(tile.getPath(), true);

        if (textureURL != null)
        {
            this.loadTexture(tile, textureURL);
        }
    }

    protected void requestTexture(DrawContext dc, TextureTile tile)
    {
        Vec4 centroid = tile.getCentroidPoint(dc.getGlobe());
        Vec4 referencePoint = this.getReferencePoint(dc);
        if (referencePoint != null)
            tile.setPriority(centroid.distanceTo3(referencePoint));

        RequestTask task = new RequestTask(tile, this);
        this.getRequestQ().add(task);
    }

    private static class RequestTask extends TileTask
    {
        private final RPFTiledImageLayer layer;

        private RequestTask(TextureTile tile, RPFTiledImageLayer layer)
        {
            super(tile);
            this.layer = layer;
        }

        public void run()
        {
            final TextureTile tile = getTile();

            // TODO: check to ensure load is still needed

            final java.net.URL textureURL = WorldWind.getDataFileStore().findFile(tile.getPath(), false);
            if (textureURL != null)
            {
                if (this.layer.loadTexture(tile, textureURL))
                {
                    layer.getLevels().unmarkResourceAbsent(tile);
                    this.layer.firePropertyChange(AVKey.LAYER, null, this);
                    return;
                }
                else
                {
                    // Assume that something's wrong with the file and delete it.
                    gov.nasa.worldwind.WorldWind.getDataFileStore().removeFile(textureURL);
                    layer.getLevels().markResourceAbsent(tile);
                    String message = Logging.getMessage("generic.DeletedCorruptDataFile", textureURL);
                    Logging.logger().info(message);
                }
            }

            this.layer.downloadTexture(tile);
        }
    }

    private boolean loadTexture(TextureTile tile, java.net.URL textureURL)
    {
        if (WWIO.isFileOutOfDate(textureURL, tile.getLevel().getExpiryTime()))
        {
            // The file has expired. Delete it then request download of newer.
            gov.nasa.worldwind.WorldWind.getDataFileStore().removeFile(textureURL);
            String message = Logging.getMessage("generic.DataFileExpired", textureURL);
            Logging.logger().fine(message);
            return false;
        }

        TextureData textureData;

        synchronized (this.fileLock)
        {
            textureData = readTexture(textureURL, this.isUseMipMaps());
        }

        if (textureData == null)
            return false;

        tile.setTextureData(textureData);
        if (tile.getLevelNumber() != 0 || !this.isRetainLevelZeroTiles())
            this.addTileToCache(tile);

        return true;
    }

    private static TextureData readTexture(java.net.URL url, boolean useMipMaps)
    {
        try
        {
            return OGLUtil.newTextureData(Configuration.getMaxCompatibleGLProfile(), url, useMipMaps);
        }
        catch (Exception e)
        {
            String msg = Logging.getMessage("layers.TextureLayer.ExceptionAttemptingToReadTextureFile", url.toString());
            Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
            return null;
        }
    }

    private void addTileToCache(TextureTile tile)
    {
        TextureTile.getMemoryCache().add(tile.getTileKey(), tile);
    }

    protected void downloadTexture(final TextureTile tile)
    {
        RPFGenerator.RPFServiceInstance service = this.rpfGenerator.getServiceInstance();
        if (service == null)
            return;

        java.net.URL url;
        try
        {
            url = tile.getResourceURL();
        }
        catch (java.net.MalformedURLException e)
        {
            Logging.logger().log(java.util.logging.Level.SEVERE,
                Logging.getMessage("layers.TextureLayer.ExceptionCreatingTextureUrl", tile), e);
            return;
        }

        if (WorldWind.getRetrievalService().isAvailable())
        {
            Retriever retriever = new RPFRetriever(service, url, new DownloadPostProcessor(tile, this));
            // Apply any overridden timeouts.
            Integer srl = AVListImpl.getIntegerValue(this, AVKey.RETRIEVAL_QUEUE_STALE_REQUEST_LIMIT);
            if (srl != null && srl > 0)
                retriever.setStaleRequestLimit(srl);
            WorldWind.getRetrievalService().runRetriever(retriever, tile.getPriority());
        }
        else
        {
            DownloadTask task = new DownloadTask(service, url, tile, this);
            this.getRequestQ().add(task);
        }
    }

    private static class DownloadPostProcessor extends AbstractRetrievalPostProcessor
    {
        private final TextureTile tile;
        private final RPFTiledImageLayer layer;

        public DownloadPostProcessor(TextureTile tile, RPFTiledImageLayer layer)
        {
            this.tile = tile;
            this.layer = layer;
        }

        @Override
        protected void markResourceAbsent()
        {
            this.layer.getLevels().markResourceAbsent(this.tile);
        }

        @Override
        protected Object getFileLock()
        {
            return this.layer.fileLock;
        }

        protected File doGetOutputFile()
        {
            return WorldWind.getDataFileStore().newFile(this.tile.getPath());
        }

        @Override
        protected ByteBuffer handleSuccessfulRetrieval()
        {
            ByteBuffer buffer = super.handleSuccessfulRetrieval();

            if (buffer != null)
            {
                // Fire a property change to denote that the layer's backing data has changed.
                this.layer.firePropertyChange(AVKey.LAYER, null, this);
            }

            return buffer;
        }

        @Override
        protected boolean validateResponseCode()
        {
            if (this.getRetriever() instanceof RPFRetriever)
                return ((RPFRetriever) this.getRetriever()).getResponseCode() == RPFRetriever.RESPONSE_CODE_OK;
            else
                return super.validateResponseCode();
        }

        @Override
        protected ByteBuffer handleTextContent() throws IOException
        {
            this.markResourceAbsent();

            return super.handleTextContent();
        }
    }

    private static class DownloadTask extends TileTask
    {
        private final RPFGenerator.RPFServiceInstance service;
        private final java.net.URL url;
        private final RPFTiledImageLayer layer;

        private DownloadTask(RPFGenerator.RPFServiceInstance service, java.net.URL url, TextureTile tile,
            RPFTiledImageLayer layer)
        {
            super(tile);
            this.service = service;
            this.url = url;
            this.layer = layer;
        }

        public void run()
        {
            final TextureTile tile = getTile();
            try
            {
                ByteBuffer buffer = createImage(this.service, this.url);
                if (buffer != null)
                {
                    final File outFile = WorldWind.getDataFileStore().newFile(tile.getPath());
                    if (outFile != null)
                    {
                        this.layer.saveBuffer(buffer, outFile);
                    }
                }
            }
            catch (Exception e)
            {
                Logging.logger().log(
                    java.util.logging.Level.SEVERE, "layers.TextureLayer.ExceptionAttemptingToCreateTileImage", e);
                this.layer.getLevels().markResourceAbsent(tile);
            }
        }
    }

    private static ByteBuffer createImage(RPFGenerator.RPFServiceInstance service, java.net.URL url)
        throws java.io.IOException
    {
        ByteBuffer buffer = null;
        BufferedImage bufferedImage = service.serviceRequest(url);
        if (bufferedImage != null)
        {
            buffer = DDSCompressor.compressImage(bufferedImage);
        }

        return buffer;
    }

    private void saveBuffer(ByteBuffer buffer, File outFile) throws java.io.IOException
    {
        synchronized (this.fileLock) // sychronized with read of file in RequestTask.run()
        {
            WWIO.saveBuffer(buffer, outFile);
        }
    }

    private static class TileTask implements Runnable, Comparable
    {
        private final TextureTile tile;

        private TileTask(TextureTile tile)
        {
            this.tile = tile;
        }

        public final TextureTile getTile()
        {
            return this.tile;
        }

        public void run()
        {
        }

        /**
         * @param that the task to compare
         *
         * @return -1 if this less than that, 1 if greater than, 0 if equal
         *
         * @throws IllegalArgumentException if that is null
         */
        public int compareTo(TileTask that)
        {
            if (that == null)
            {
                String msg = Logging.getMessage("nullValue.RequestTaskIsNull");
                Logging.logger().severe(msg);
                throw new IllegalArgumentException(msg);
            }
            return this.tile.getPriority() == that.tile.getPriority() ? 0 :
                this.tile.getPriority() < that.tile.getPriority() ? -1 : 1;
        }

        public boolean equals(Object o)
        {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            final TileTask that = (TileTask) o;

            // Don't include layer in comparison so that requests are shared among layers
            return !(tile != null ? !tile.equals(that.tile) : that.tile != null);
        }

        public int hashCode()
        {
            return (tile != null ? tile.hashCode() : 0);
        }

        public String toString()
        {
            return this.tile.getPath();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy