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

src.gov.nasa.worldwind.layers.BasicTiledImageLayer Maven / Gradle / Ivy

Go to download

World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.

There is a newer version: 2.0.0-986
Show 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;

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.event.BulkRetrievalListener;
import gov.nasa.worldwind.exception.WWRuntimeException;
import gov.nasa.worldwind.formats.dds.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.ogc.wms.WMSCapabilities;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.retrieve.*;
import gov.nasa.worldwind.util.*;
import org.w3c.dom.*;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Map;

/**
 * @author tag
 * @version $Id: BasicTiledImageLayer.java 1931 2014-04-14 21:31:43Z tgaskins $
 */
public class BasicTiledImageLayer extends TiledImageLayer implements BulkRetrievable
{
    protected final Object fileLock = new Object();

    // Layer resource properties.
    protected static final int RESOURCE_ID_OGC_CAPABILITIES = 1;

    public BasicTiledImageLayer(LevelSet levelSet)
    {
        super(levelSet);
    }

    public BasicTiledImageLayer(AVList params)
    {
        this(new LevelSet(params));

        String s = params.getStringValue(AVKey.DISPLAY_NAME);
        if (s != null)
            this.setName(s);

        String[] strings = (String[]) params.getValue(AVKey.AVAILABLE_IMAGE_FORMATS);
        if (strings != null && strings.length > 0)
            this.setAvailableImageFormats(strings);

        s = params.getStringValue(AVKey.TEXTURE_FORMAT);
        if (s != null)
            this.setTextureFormat(s);

        Double d = (Double) params.getValue(AVKey.OPACITY);
        if (d != null)
            this.setOpacity(d);

        d = (Double) params.getValue(AVKey.MAX_ACTIVE_ALTITUDE);
        if (d != null)
            this.setMaxActiveAltitude(d);

        d = (Double) params.getValue(AVKey.MIN_ACTIVE_ALTITUDE);
        if (d != null)
            this.setMinActiveAltitude(d);

        d = (Double) params.getValue(AVKey.MAP_SCALE);
        if (d != null)
            this.setValue(AVKey.MAP_SCALE, d);

        d = (Double) params.getValue(AVKey.DETAIL_HINT);
        if (d != null)
            this.setDetailHint(d);

        Boolean b = (Boolean) params.getValue(AVKey.FORCE_LEVEL_ZERO_LOADS);
        if (b != null)
            this.setForceLevelZeroLoads(b);

        b = (Boolean) params.getValue(AVKey.RETAIN_LEVEL_ZERO_TILES);
        if (b != null)
            this.setRetainLevelZeroTiles(b);

        b = (Boolean) params.getValue(AVKey.NETWORK_RETRIEVAL_ENABLED);
        if (b != null)
            this.setNetworkRetrievalEnabled(b);

        b = (Boolean) params.getValue(AVKey.USE_MIP_MAPS);
        if (b != null)
            this.setUseMipMaps(b);

        b = (Boolean) params.getValue(AVKey.USE_TRANSPARENT_TEXTURES);
        if (b != null)
            this.setUseTransparentTextures(b);

        Object o = params.getValue(AVKey.URL_CONNECT_TIMEOUT);
        if (o != null)
            this.setValue(AVKey.URL_CONNECT_TIMEOUT, o);

        o = params.getValue(AVKey.URL_READ_TIMEOUT);
        if (o != null)
            this.setValue(AVKey.URL_READ_TIMEOUT, o);

        o = params.getValue(AVKey.RETRIEVAL_QUEUE_STALE_REQUEST_LIMIT);
        if (o != null)
            this.setValue(AVKey.RETRIEVAL_QUEUE_STALE_REQUEST_LIMIT, o);

        ScreenCredit sc = (ScreenCredit) params.getValue(AVKey.SCREEN_CREDIT);
        if (sc != null)
            this.setScreenCredit(sc);

        if (params.getValue(AVKey.TRANSPARENCY_COLORS) != null)
            this.setValue(AVKey.TRANSPARENCY_COLORS, params.getValue(AVKey.TRANSPARENCY_COLORS));

        this.setValue(AVKey.CONSTRUCTION_PARAMETERS, params.copy());

        // If any resources should be retrieved for this Layer, start a task to retrieve those resources, and initialize
        // this Layer once those resources are retrieved.
        if (this.isRetrieveResources())
        {
            this.startResourceRetrieval();
        }
    }

    public BasicTiledImageLayer(Document dom, AVList params)
    {
        this(dom.getDocumentElement(), params);
    }

    public BasicTiledImageLayer(Element domElement, AVList params)
    {
        this(getParamsFromDocument(domElement, params));
    }

    public BasicTiledImageLayer(String restorableStateInXml)
    {
        this(restorableStateToParams(restorableStateInXml));

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

        this.doRestoreState(rs, null);
    }

    protected static AVList getParamsFromDocument(Element domElement, AVList params)
    {
        if (domElement == null)
        {
            String message = Logging.getMessage("nullValue.DocumentIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        if (params == null)
            params = new AVListImpl();

        getTiledImageLayerConfigParams(domElement, params);
        setFallbacks(params);

        return params;
    }

    protected static void setFallbacks(AVList params)
    {
        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, 19); // approximately 0.1 meters per pixel

        if (params.getValue(AVKey.NUM_EMPTY_LEVELS) == null)
            params.setValue(AVKey.NUM_EMPTY_LEVELS, 0);
    }

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

        if (textureURL != null && !this.isTextureFileExpired(tile, textureURL, this.getDataFileStore()))
        {
            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 = this.createRequestTask(tile);
        this.getRequestQ().add(task);
    }

    protected RequestTask createRequestTask(TextureTile tile)
    {
        return new RequestTask(tile, this);
    }

    protected static class RequestTask implements Runnable, Comparable
    {
        protected final BasicTiledImageLayer layer;
        protected final TextureTile tile;

        protected RequestTask(TextureTile tile, BasicTiledImageLayer layer)
        {
            this.layer = layer;
            this.tile = tile;
        }

        public void run()
        {
            if (Thread.currentThread().isInterrupted())
                return; // the task was cancelled because it's a duplicate or for some other reason

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

            this.layer.retrieveTexture(this.tile, this.layer.createDownloadPostProcessor(this.tile));
        }

        /**
         * @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(RequestTask 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 RequestTask that = (RequestTask) 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.toString();
        }
    }

    protected boolean isTextureFileExpired(TextureTile tile, java.net.URL textureURL, FileStore fileStore)
    {
        if (!WWIO.isFileOutOfDate(textureURL, tile.getLevel().getExpiryTime()))
            return false;

        // The file has expired. Delete it.
        fileStore.removeFile(textureURL);
        String message = Logging.getMessage("generic.DataFileExpired", textureURL);
        Logging.logger().fine(message);
        return true;
    }

    protected boolean loadTexture(TextureTile tile, java.net.URL textureURL)
    {
        TextureData textureData;

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

        if (textureData == null)
            return false;

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

        return true;
    }

    /**
     * Reads and returns the texture data at the specified URL, optionally converting it to the specified format and
     * generating mip-maps. If textureFormat is a recognized mime type, this returns the texture data in
     * the specified format. Otherwise, this returns the texture data in its native format. If useMipMaps
     * is true, this generates mip maps for any non-DDS texture data, and uses any mip-maps contained in DDS texture
     * data.
     * 

* Supported texture formats are as follows:

  • image/dds - Returns DDS texture data, converting * the data to DDS if necessary. If the data is already in DDS format it's returned as-is.
* * @param url the URL referencing the texture data to read. * @param textureFormat the texture data format to return. * @param useMipMaps true to generate mip-maps for the texture data or use mip maps already in the texture data, * and false to read the texture data without generating or using mip-maps. * * @return TextureData the texture data from the specified URL, in the specified format and with mip-maps. */ protected TextureData readTexture(java.net.URL url, String textureFormat, boolean useMipMaps) { try { // If the caller has enabled texture compression, and the texture data is not a DDS file, then use read the // texture data and convert it to DDS. if ("image/dds".equalsIgnoreCase(textureFormat) && !url.toString().toLowerCase().endsWith("dds")) { // Configure a DDS compressor to generate mipmaps based according to the 'useMipMaps' parameter, and // convert the image URL to a compressed DDS format. DXTCompressionAttributes attributes = DDSCompressor.getDefaultCompressionAttributes(); attributes.setBuildMipmaps(useMipMaps); ByteBuffer buffer = DDSCompressor.compressImageURL(url, attributes); return OGLUtil.newTextureData(Configuration.getMaxCompatibleGLProfile(), WWIO.getInputStreamFromByteBuffer(buffer), useMipMaps); } // If the caller has disabled texture compression, or if the texture data is already a DDS file, then read // the texture data without converting it. else { return OGLUtil.newTextureData(Configuration.getMaxCompatibleGLProfile(), url, useMipMaps); } } catch (Exception e) { String msg = Logging.getMessage("layers.TextureLayer.ExceptionAttemptingToReadTextureFile", url); Logging.logger().log(java.util.logging.Level.SEVERE, msg, e); return null; } } protected void addTileToCache(TextureTile tile) { TextureTile.getMemoryCache().add(tile.getTileKey(), tile); } // *** Bulk download *** // *** Bulk download *** // *** Bulk download *** /** * Start a new {@link BulkRetrievalThread} that downloads all imagery for a given sector and resolution to the * current World Wind file cache, without downloading imagery that is already in the cache. *

* This method creates and starts a thread to perform the download. A reference to the thread is returned. To create * a downloader that has not been started, construct a {@link BasicTiledImageLayerBulkDownloader}. *

* Note that the target resolution must be provided in radians of latitude per texel, which is the resolution in * meters divided by the globe radius. * * @param sector the sector to download imagery for. * @param resolution the target resolution, provided in radians of latitude per texel. * @param listener an optional retrieval listener. May be null. * * @return the {@link BulkRetrievalThread} executing the retrieval or null if the specified sector does * not intersect the layer bounding sector. * * @throws IllegalArgumentException if the sector is null or the resolution is less than zero. * @see BasicTiledImageLayerBulkDownloader */ public BulkRetrievalThread makeLocal(Sector sector, double resolution, BulkRetrievalListener listener) { return makeLocal(sector, resolution, null, listener); } /** * Start a new {@link BulkRetrievalThread} that downloads all imagery for a given sector and resolution to a * specified {@link FileStore}, without downloading imagery that is already in the file store. *

* This method creates and starts a thread to perform the download. A reference to the thread is returned. To create * a downloader that has not been started, construct a {@link BasicTiledImageLayerBulkDownloader}. *

* Note that the target resolution must be provided in radians of latitude per texel, which is the resolution in * meters divided by the globe radius. * * @param sector the sector to download data for. * @param resolution the target resolution, provided in radians of latitude per texel. * @param fileStore the file store in which to place the downloaded imagery. If null the current World Wind file * cache is used. * @param listener an optional retrieval listener. May be null. * * @return the {@link BulkRetrievalThread} executing the retrieval or null if the specified sector does * not intersect the layer bounding sector. * * @throws IllegalArgumentException if the sector is null or the resolution is less than zero. * @see BasicTiledImageLayerBulkDownloader */ public BulkRetrievalThread makeLocal(Sector sector, double resolution, FileStore fileStore, BulkRetrievalListener listener) { Sector targetSector = sector != null ? getLevels().getSector().intersection(sector) : null; if (targetSector == null) return null; BasicTiledImageLayerBulkDownloader thread = new BasicTiledImageLayerBulkDownloader(this, targetSector, resolution, fileStore != null ? fileStore : this.getDataFileStore(), listener); thread.setDaemon(true); thread.start(); return thread; } /** * Get the estimated size in bytes of the imagery not in the World Wind file cache for the given sector and * resolution. *

* Note that the target resolution must be provided in radians of latitude per texel, which is the resolution in * meters divided by the globe radius. * * @param sector the sector to estimate. * @param resolution the target resolution, provided in radians of latitude per texel. * * @return the estimated size in bytes of the missing imagery. * * @throws IllegalArgumentException if the sector is null or the resolution is less than zero. */ public long getEstimatedMissingDataSize(Sector sector, double resolution) { return this.getEstimatedMissingDataSize(sector, resolution, null); } /** * Get the estimated size in bytes of the imagery not in a specified file store for a specified sector and * resolution. *

* Note that the target resolution must be provided in radians of latitude per texel, which is the resolution in * meters divided by the globe radius. * * @param sector the sector to estimate. * @param resolution the target resolution, provided in radians of latitude per texel. * @param fileStore the file store to examine. If null the current World Wind file cache is used. * * @return the estimated size in byte of the missing imagery. * * @throws IllegalArgumentException if the sector is null or the resolution is less than zero. */ public long getEstimatedMissingDataSize(Sector sector, double resolution, FileStore fileStore) { Sector targetSector = sector != null ? getLevels().getSector().intersection(sector) : null; if (targetSector == null) return 0; BasicTiledImageLayerBulkDownloader downloader = new BasicTiledImageLayerBulkDownloader(this, sector, resolution, fileStore != null ? fileStore : this.getDataFileStore(), null); return downloader.getEstimatedMissingDataSize(); } // *** Tile download *** // *** Tile download *** // *** Tile download *** protected void retrieveTexture(TextureTile tile, DownloadPostProcessor postProcessor) { if (this.getValue(AVKey.RETRIEVER_FACTORY_LOCAL) != null) this.retrieveLocalTexture(tile, postProcessor); else // Assume it's remote, which handles the legacy cases. this.retrieveRemoteTexture(tile, postProcessor); } protected void retrieveLocalTexture(TextureTile tile, DownloadPostProcessor postProcessor) { if (!WorldWind.getLocalRetrievalService().isAvailable()) return; RetrieverFactory retrieverFactory = (RetrieverFactory) this.getValue(AVKey.RETRIEVER_FACTORY_LOCAL); if (retrieverFactory == null) return; AVListImpl avList = new AVListImpl(); avList.setValue(AVKey.SECTOR, tile.getSector()); avList.setValue(AVKey.WIDTH, tile.getWidth()); avList.setValue(AVKey.HEIGHT, tile.getHeight()); avList.setValue(AVKey.FILE_NAME, tile.getPath()); Retriever retriever = retrieverFactory.createRetriever(avList, postProcessor); WorldWind.getLocalRetrievalService().runRetriever(retriever, tile.getPriority()); } protected void retrieveRemoteTexture(TextureTile tile, DownloadPostProcessor postProcessor) { if (!this.isNetworkRetrievalEnabled()) { this.getLevels().markResourceAbsent(tile); return; } if (!WorldWind.getRetrievalService().isAvailable()) return; java.net.URL url; try { url = tile.getResourceURL(); if (url == null) return; if (WorldWind.getNetworkStatus().isHostUnavailable(url)) { this.getLevels().markResourceAbsent(tile); return; } } catch (java.net.MalformedURLException e) { Logging.logger().log(java.util.logging.Level.SEVERE, Logging.getMessage("layers.TextureLayer.ExceptionCreatingTextureUrl", tile), e); return; } Retriever retriever; if (postProcessor == null) postProcessor = this.createDownloadPostProcessor(tile); retriever = URLRetriever.createRetriever(url, postProcessor); if (retriever == null) { Logging.logger().severe( Logging.getMessage("layers.TextureLayer.UnknownRetrievalProtocol", url.toString())); return; } retriever.setValue(URLRetriever.EXTRACT_ZIP_ENTRY, "true"); // supports legacy layers // Apply any overridden timeouts. Integer cto = AVListImpl.getIntegerValue(this, AVKey.URL_CONNECT_TIMEOUT); if (cto != null && cto > 0) retriever.setConnectTimeout(cto); Integer cro = AVListImpl.getIntegerValue(this, AVKey.URL_READ_TIMEOUT); if (cro != null && cro > 0) retriever.setReadTimeout(cro); 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()); } protected DownloadPostProcessor createDownloadPostProcessor(TextureTile tile) { return new DownloadPostProcessor(tile, this); } protected static class DownloadPostProcessor extends AbstractRetrievalPostProcessor { protected final TextureTile tile; protected final BasicTiledImageLayer layer; protected final FileStore fileStore; public DownloadPostProcessor(TextureTile tile, BasicTiledImageLayer layer) { this(tile, layer, null); } public DownloadPostProcessor(TextureTile tile, BasicTiledImageLayer layer, FileStore fileStore) { //noinspection RedundantCast super((AVList) layer); this.tile = tile; this.layer = layer; this.fileStore = fileStore; } protected FileStore getFileStore() { return this.fileStore != null ? this.fileStore : this.layer.getDataFileStore(); } @Override protected void markResourceAbsent() { this.layer.getLevels().markResourceAbsent(this.tile); } @Override protected Object getFileLock() { return this.layer.fileLock; } @Override protected File doGetOutputFile() { return this.getFileStore().newFile(this.tile.getPath()); } @Override protected ByteBuffer handleSuccessfulRetrieval() { ByteBuffer buffer = super.handleSuccessfulRetrieval(); if (buffer != null) { // We've successfully cached data. Check if there's a configuration file for this layer, create one // if there's not. this.layer.writeConfigurationFile(this.getFileStore()); // 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 ByteBuffer handleTextContent() throws IOException { this.markResourceAbsent(); return super.handleTextContent(); } } //**************************************************************// //******************** Non-Tile Resource Retrieval ***********// //**************************************************************// /** * Retrieves any non-tile resources associated with this Layer, either online or in the local filesystem, and * initializes properties of this Layer using those resources. This returns a key indicating the retrieval state: * {@link gov.nasa.worldwind.avlist.AVKey#RETRIEVAL_STATE_SUCCESSFUL} indicates the retrieval succeeded, {@link * gov.nasa.worldwind.avlist.AVKey#RETRIEVAL_STATE_ERROR} indicates the retrieval failed with errors, and * null indicates the retrieval state is unknown. This method may invoke blocking I/O operations, and * therefore should not be executed from the rendering thread. * * @return {@link gov.nasa.worldwind.avlist.AVKey#RETRIEVAL_STATE_SUCCESSFUL} if the retrieval succeeded, {@link * gov.nasa.worldwind.avlist.AVKey#RETRIEVAL_STATE_ERROR} if the retrieval failed with errors, and * null if the retrieval state is unknown. */ protected String retrieveResources() { // This Layer has no construction parameters, so there is no description of what to retrieve. Return a key // indicating the resources have been successfully retrieved, though there is nothing to retrieve. AVList params = (AVList) this.getValue(AVKey.CONSTRUCTION_PARAMETERS); if (params == null) { String message = Logging.getMessage("nullValue.ConstructionParametersIsNull"); Logging.logger().warning(message); return AVKey.RETRIEVAL_STATE_SUCCESSFUL; } // This Layer has no OGC Capabilities URL in its construction parameters. Return a key indicating the resources // have been successfully retrieved, though there is nothing to retrieve. URL url = DataConfigurationUtils.getOGCGetCapabilitiesURL(params); if (url == null) { String message = Logging.getMessage("nullValue.CapabilitiesURLIsNull"); Logging.logger().warning(message); return AVKey.RETRIEVAL_STATE_SUCCESSFUL; } // Get the service's OGC Capabilities resource from the session cache, or initiate a retrieval to fetch it. // SessionCacheUtils.getOrRetrieveSessionCapabilities() returns null if it initiated a // retrieval, or if the OGC Capabilities URL is unavailable. // // Note that we use the URL's String representation as the cache key. We cannot use the URL itself, because // the cache invokes the methods Object.hashCode() and Object.equals() on the cache key. URL's implementations // of hashCode() and equals() perform blocking IO calls. World Wind does not perform blocking calls during // rendering, and this method is likely to be called from the rendering thread. WMSCapabilities caps; if (this.isNetworkRetrievalEnabled()) caps = SessionCacheUtils.getOrRetrieveSessionCapabilities(url, WorldWind.getSessionCache(), url.toString(), null, RESOURCE_ID_OGC_CAPABILITIES, null, null); else caps = SessionCacheUtils.getSessionCapabilities(WorldWind.getSessionCache(), url.toString(), url.toString()); // The OGC Capabilities resource retrieval is either currently running in another thread, or has failed. In // either case, return null indicating that that the retrieval was not successful, and we should try again // later. if (caps == null) return null; // We have successfully retrieved this Layer's OGC Capabilities resource. Initialize this Layer using the // Capabilities document, and return a key indicating the retrieval has succeeded. this.initFromOGCCapabilitiesResource(caps, params); return AVKey.RETRIEVAL_STATE_SUCCESSFUL; } /** * Initializes this Layer's expiry time property from the specified WMS Capabilities document and parameter list * describing the WMS layer names associated with this Layer. This method is thread safe; it synchronizes changes to * this Layer by wrapping the appropriate method calls in {@link SwingUtilities#invokeLater(Runnable)}. * * @param caps the WMS Capabilities document retrieved from this Layer's WMS server. * @param params the parameter list describing the WMS layer names associated with this Layer. * * @throws IllegalArgumentException if either the Capabilities or the parameter list is null. */ protected void initFromOGCCapabilitiesResource(WMSCapabilities caps, AVList params) { if (caps == null) { String message = Logging.getMessage("nullValue.CapabilitiesIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (params == null) { String message = Logging.getMessage("nullValue.ParametersIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } String[] names = DataConfigurationUtils.getOGCLayerNames(params); if (names == null || names.length == 0) return; final Long expiryTime = caps.getLayerLatestLastUpdateTime(names); if (expiryTime == null) return; // Synchronize changes to this Layer with the Event Dispatch Thread. SwingUtilities.invokeLater(new Runnable() { public void run() { BasicTiledImageLayer.this.setExpiryTime(expiryTime); BasicTiledImageLayer.this.firePropertyChange(AVKey.LAYER, null, BasicTiledImageLayer.this); } }); } /** * Returns a boolean value indicating if this Layer should retrieve any non-tile resources, either online or in the * local filesystem, and initialize itself using those resources. * * @return true if this Layer should retrieve any non-tile resources, and false * otherwise. */ protected boolean isRetrieveResources() { AVList params = (AVList) this.getValue(AVKey.CONSTRUCTION_PARAMETERS); if (params == null) return false; Boolean b = (Boolean) params.getValue(AVKey.RETRIEVE_PROPERTIES_FROM_SERVICE); return b != null && b; } /** Starts retrieving non-tile resources associated with this Layer in a non-rendering thread. */ protected void startResourceRetrieval() { Thread t = new Thread(new Runnable() { @Override public void run() { retrieveResources(); } }); t.setName("Capabilities retrieval for " + this.getName()); t.start(); } //**************************************************************// //******************** Configuration *************************// //**************************************************************// protected void writeConfigurationFile(FileStore fileStore) { // TODO: configurable max attempts for creating a configuration file. try { AVList configParams = this.getConfigurationParams(null); this.writeConfigurationParams(fileStore, configParams); } catch (Exception e) { String message = Logging.getMessage("generic.ExceptionAttemptingToWriteConfigurationFile"); Logging.logger().log(java.util.logging.Level.SEVERE, message, e); } } protected void writeConfigurationParams(FileStore fileStore, AVList params) { // Determine what the configuration file name should be based on the configuration parameters. Assume an XML // configuration document type, and append the XML file suffix. String fileName = DataConfigurationUtils.getDataConfigFilename(params, ".xml"); if (fileName == null) { String message = Logging.getMessage("nullValue.FilePathIsNull"); Logging.logger().severe(message); throw new WWRuntimeException(message); } // Check if this component needs to write a configuration file. This happens outside of the synchronized block // to improve multithreaded performance for the common case: the configuration file already exists, this just // need to check that it's there and return. If the file exists but is expired, do not remove it - this // removes the file inside the synchronized block below. if (!this.needsConfigurationFile(fileStore, fileName, params, false)) return; synchronized (this.fileLock) { // Check again if the component needs to write a configuration file, potentially removing any existing file // which has expired. This additional check is necessary because the file could have been created by // another thread while we were waiting for the lock. if (!this.needsConfigurationFile(fileStore, fileName, params, true)) return; this.doWriteConfigurationParams(fileStore, fileName, params); } } protected void doWriteConfigurationParams(FileStore fileStore, String fileName, AVList params) { java.io.File file = fileStore.newFile(fileName); if (file == null) { String message = Logging.getMessage("generic.CannotCreateFile", fileName); Logging.logger().severe(message); throw new WWRuntimeException(message); } Document doc = this.createConfigurationDocument(params); WWXML.saveDocumentToFile(doc, file.getPath()); String message = Logging.getMessage("generic.ConfigurationFileCreated", fileName); Logging.logger().fine(message); } protected boolean needsConfigurationFile(FileStore fileStore, String fileName, AVList params, boolean removeIfExpired) { long expiryTime = this.getExpiryTime(); if (expiryTime <= 0) expiryTime = AVListImpl.getLongValue(params, AVKey.EXPIRY_TIME, 0L); return !DataConfigurationUtils.hasDataConfigFile(fileStore, fileName, removeIfExpired, expiryTime); } protected AVList getConfigurationParams(AVList params) { if (params == null) params = new AVListImpl(); // Gather all the construction parameters if they are available. AVList constructionParams = (AVList) this.getValue(AVKey.CONSTRUCTION_PARAMETERS); if (constructionParams != null) params.setValues(constructionParams); // Gather any missing LevelSet parameters from the LevelSet itself. DataConfigurationUtils.getLevelSetConfigParams(this.getLevels(), params); return params; } protected Document createConfigurationDocument(AVList params) { return createTiledImageLayerConfigDocument(params); } //**************************************************************// //******************** Restorable Support ********************// //**************************************************************// public String getRestorableState() { // We only create a restorable state XML if this elevation model was constructed with an AVList. AVList constructionParams = (AVList) this.getValue(AVKey.CONSTRUCTION_PARAMETERS); if (constructionParams == null) return null; RestorableSupport rs = RestorableSupport.newRestorableSupport(); // Creating a new RestorableSupport failed. RestorableSupport logged the problem, so just return null. if (rs == null) return null; this.doGetRestorableState(rs, null); return rs.getStateAsXml(); } public void restoreState(String stateInXml) { String message = Logging.getMessage("RestorableSupport.RestoreRequiresConstructor"); Logging.logger().severe(message); throw new UnsupportedOperationException(message); } protected void doGetRestorableState(RestorableSupport rs, RestorableSupport.StateObject context) { AVList constructionParams = (AVList) this.getValue(AVKey.CONSTRUCTION_PARAMETERS); if (constructionParams != null) { for (Map.Entry avp : constructionParams.getEntries()) { this.getRestorableStateForAVPair(avp.getKey(), avp.getValue(), rs, context); } } rs.addStateValueAsBoolean(context, "Layer.Enabled", this.isEnabled()); rs.addStateValueAsDouble(context, "Layer.Opacity", this.getOpacity()); rs.addStateValueAsDouble(context, "Layer.MinActiveAltitude", this.getMinActiveAltitude()); rs.addStateValueAsDouble(context, "Layer.MaxActiveAltitude", this.getMaxActiveAltitude()); rs.addStateValueAsBoolean(context, "Layer.NetworkRetrievalEnabled", this.isNetworkRetrievalEnabled()); rs.addStateValueAsString(context, "Layer.Name", this.getName()); rs.addStateValueAsBoolean(context, "TiledImageLayer.UseMipMaps", this.isUseMipMaps()); rs.addStateValueAsBoolean(context, "TiledImageLayer.UseTransparentTextures", this.isUseTransparentTextures()); RestorableSupport.StateObject so = rs.addStateObject(context, "avlist"); for (Map.Entry avp : this.getEntries()) { this.getRestorableStateForAVPair(avp.getKey(), avp.getValue(), rs, so); } } public void getRestorableStateForAVPair(String key, Object value, RestorableSupport rs, RestorableSupport.StateObject context) { if (value == null) return; if (key.equals(AVKey.CONSTRUCTION_PARAMETERS)) return; if (value instanceof LatLon) { rs.addStateValueAsLatLon(context, key, (LatLon) value); } else if (value instanceof Sector) { rs.addStateValueAsSector(context, key, (Sector) value); } else if (value instanceof Color) { rs.addStateValueAsColor(context, key, (Color) value); } else { super.getRestorableStateForAVPair(key, value, rs, context); } } protected void doRestoreState(RestorableSupport rs, RestorableSupport.StateObject context) { Boolean b = rs.getStateValueAsBoolean(context, "Layer.Enabled"); if (b != null) this.setEnabled(b); Double d = rs.getStateValueAsDouble(context, "Layer.Opacity"); if (d != null) this.setOpacity(d); d = rs.getStateValueAsDouble(context, "Layer.MinActiveAltitude"); if (d != null) this.setMinActiveAltitude(d); d = rs.getStateValueAsDouble(context, "Layer.MaxActiveAltitude"); if (d != null) this.setMaxActiveAltitude(d); b = rs.getStateValueAsBoolean(context, "Layer.NetworkRetrievalEnabled"); if (b != null) this.setNetworkRetrievalEnabled(b); String s = rs.getStateValueAsString(context, "Layer.Name"); if (s != null) this.setName(s); b = rs.getStateValueAsBoolean(context, "TiledImageLayer.UseMipMaps"); if (b != null) this.setUseMipMaps(b); b = rs.getStateValueAsBoolean(context, "TiledImageLayer.UseTransparentTextures"); if (b != null) this.setUseTransparentTextures(b); RestorableSupport.StateObject so = rs.getStateObject(context, "avlist"); if (so != null) { RestorableSupport.StateObject[] avpairs = rs.getAllStateObjects(so, ""); if (avpairs != null) { for (RestorableSupport.StateObject avp : avpairs) { if (avp != null) this.doRestoreStateForObject(rs, avp); } } } } @SuppressWarnings({"UnusedDeclaration"}) protected void doRestoreStateForObject(RestorableSupport rs, RestorableSupport.StateObject so) { if (so == null) return; this.setValue(so.getName(), so.getValue()); } protected static AVList restorableStateToParams(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(); restoreStateForParams(rs, null, params); return params; } protected static void restoreStateForParams(RestorableSupport rs, RestorableSupport.StateObject context, AVList params) { String s = rs.getStateValueAsString(context, AVKey.DATA_CACHE_NAME); if (s != null) params.setValue(AVKey.DATA_CACHE_NAME, s); s = rs.getStateValueAsString(context, AVKey.SERVICE); if (s != null) params.setValue(AVKey.SERVICE, s); s = rs.getStateValueAsString(context, AVKey.DATASET_NAME); if (s != null) params.setValue(AVKey.DATASET_NAME, s); s = rs.getStateValueAsString(context, AVKey.FORMAT_SUFFIX); if (s != null) params.setValue(AVKey.FORMAT_SUFFIX, s); Integer i = rs.getStateValueAsInteger(context, AVKey.NUM_EMPTY_LEVELS); if (i != null) params.setValue(AVKey.NUM_EMPTY_LEVELS, i); i = rs.getStateValueAsInteger(context, AVKey.NUM_LEVELS); if (i != null) params.setValue(AVKey.NUM_LEVELS, i); i = rs.getStateValueAsInteger(context, AVKey.TILE_WIDTH); if (i != null) params.setValue(AVKey.TILE_WIDTH, i); i = rs.getStateValueAsInteger(context, AVKey.TILE_HEIGHT); if (i != null) params.setValue(AVKey.TILE_HEIGHT, i); Long lo = rs.getStateValueAsLong(context, AVKey.EXPIRY_TIME); if (lo != null) params.setValue(AVKey.EXPIRY_TIME, lo); LatLon ll = rs.getStateValueAsLatLon(context, AVKey.LEVEL_ZERO_TILE_DELTA); if (ll != null) params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, ll); ll = rs.getStateValueAsLatLon(context, AVKey.TILE_ORIGIN); if (ll != null) params.setValue(AVKey.TILE_ORIGIN, ll); Sector sector = rs.getStateValueAsSector(context, AVKey.SECTOR); if (sector != null) params.setValue(AVKey.SECTOR, sector); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy