tiledrasterproducer.java

来自「world wind java sdk 源码」· Java 代码 · 共 965 行 · 第 1/3 页

JAVA
965
字号
/* Copyright (C) 2001, 2008 United States Government as represented by
the Administrator of the National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.data;

import gov.nasa.worldwind.*;
import gov.nasa.worldwind.avlist.*;
import gov.nasa.worldwind.cache.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.util.*;

/**
 * @author dcollins
 * @version $Id: TiledRasterProducer.java 8329 2009-01-05 21:29:51Z dcollins $
 */
public abstract class TiledRasterProducer extends AbstractDataStoreProducer
{
    private static final long DEFAULT_TILED_RASTER_PRODUCER_CACHE_SIZE = 300000000L; // ~300 megabytes
    private static final int DEFAULT_TILED_RASTER_PRODUCER_LARGE_DATASET_THRESHOLD = 3000; // 3000 pixels
    private static final int DEFAULT_WRITE_THREAD_POOL_SIZE = 2;
    private static final int DEFAULT_TILE_WIDTH_AND_HEIGHT = 512;
    private static final int DEFAULT_SINGLE_LEVEL_TILE_WIDTH_AND_HEIGHT = 512;
    private static final double DEFAULT_LEVEL_ZERO_TILE_DELTA = 36d;

    // List of source data rasters.
    private java.util.List<DataRaster> dataRasterList = new java.util.ArrayList<DataRaster>();
    // Data raster caching.
    private MemoryCache rasterCache;
    // Concurrent processing helper objects.
    private final java.util.concurrent.ExecutorService tileWriteService;
    private final java.util.concurrent.Semaphore tileWriteSemaphore;
    private final Object fileLock = new Object();
    // Progress counters.
    private int tile;
    private int tileCount;

    public TiledRasterProducer(MemoryCache cache, int writeThreadPoolSize)
    {
        if (cache == null)
        {
            String message = Logging.getMessage("nullValue.CacheIsNull");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }
        if (writeThreadPoolSize < 1)
        {
            String message = Logging.getMessage("generic.ArgumentOutOfRange", "writeThreadPoolSize < 1");
            Logging.logger().severe(message);
            throw new IllegalArgumentException(message);
        }

        this.rasterCache = cache;
        this.tileWriteService = this.createDefaultTileWriteService(writeThreadPoolSize);
        this.tileWriteSemaphore = new java.util.concurrent.Semaphore(writeThreadPoolSize, true);
    }

    public TiledRasterProducer()
    {
        this(createDefaultCache(), DEFAULT_WRITE_THREAD_POOL_SIZE);
    }

    // TODO: this describes the file types the producer will read. Make that more clear in the method name.
    public String getDataSourceDescription()
    {
        DataRasterReader[] readers = this.getDataRasterReaders();
        if (readers == null || readers.length < 1)
            return "";

        // Collect all the unique format suffixes available in all readers. If a reader does not publish any
        // format suffixes, then collect it's description.
        java.util.Set<String> suffixSet = new java.util.TreeSet<String>();
        java.util.Set<String> descriptionSet = new java.util.TreeSet<String>();
        for (DataRasterReader reader : readers)
        {
            String description = reader.getDescription();
            String[] names = reader.getSuffixes();

            if (names != null && names.length > 0)
                suffixSet.addAll(java.util.Arrays.asList(names));
            else
                descriptionSet.add(description);
        }

        // Create a string representaiton of the format suffixes (or description if no suffixes are available) for
        // all readers.
        StringBuilder sb = new StringBuilder();
        for (String suffix : suffixSet)
        {
            if (sb.length() > 0)
                sb.append(", ");
            sb.append("*.").append(suffix);
        }
        for (String description : descriptionSet)
        {
            if (sb.length() > 0)
                sb.append(", ");
            sb.append(description);
        }
        return sb.toString();
    }

    public void removeProductionState()
    {
        java.io.File installLocation = this.installLocationFor(this.getStoreParameters());

        if (installLocation == null || !installLocation.exists())
        {
            String message = "Install location is null or does not exist";
            Logging.logger().warning(message);
            return;
        }

        try
        {
            WWIO.deleteDirectory(installLocation);
        }
        catch (Exception e)
        {
            String message = "Exception while removing install location";
            Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
        }
    }

    protected abstract DataRaster createDataRaster(int width, int height, Sector sector, AVList params);

    protected abstract DataRasterReader[] getDataRasterReaders();

    protected abstract DataRasterWriter[] getDataRasterWriters();
    
    protected MemoryCache getCache()
    {
        return this.rasterCache;
    }

    protected java.util.concurrent.ExecutorService getTileWriteService()
    {
        return this.tileWriteService;
    }

    protected java.util.concurrent.Semaphore getTileWriteSemaphore()
    {
        return this.tileWriteSemaphore;
    }

    protected void doStartProduction(AVList parameters) throws Exception
    {
        // Copy production parameters to prevent changes to caller's reference.
        AVList productionParams = parameters.copy();
        this.initProductionParameters(productionParams);

        // Assemble the source data rasters.
        this.assembleDataRasters();

        // Initialize the level set parameters, and create the level set.
        this.initLevelSetParameters(productionParams);
        LevelSet levelSet = new LevelSet(productionParams);
        // Install the each tiles of the LevelSet.
        this.installLevelSet(levelSet, productionParams);

        // Wait for concurrent tasks to complete.
        this.waitForInstallTileTasks();
        // Clear the raster cache.
        this.getCache().clear();

        // Install the data descriptor for this tiled raster set.
        this.installDataDescriptor(productionParams);
    }

    protected String validateProductionParameters(AVList parameters)
    {
        StringBuilder sb = new StringBuilder();

        Object o = parameters.getValue(AVKey.FILE_STORE_LOCATION);
        if (o == null || !(o instanceof String) || ((String) o).length() < 1)
            sb.append((sb.length() > 0 ? ", " : "")).append(Logging.getMessage("term.fileStoreLocation"));

        o = parameters.getValue(AVKey.DATA_CACHE_NAME);
        if (o == null || !(o instanceof String) || ((String) o).length() == 0)
            sb.append((sb.length() > 0 ? ", " : "")).append(Logging.getMessage("term.fileStoreFolder"));

        o = parameters.getValue(AVKey.DATASET_NAME);
        if (o == null || !(o instanceof String) || ((String) o).length() < 1)
            sb.append((sb.length() > 0 ? ", " : "")).append(Logging.getMessage("term.datasetName"));

        if (sb.length() == 0)
            return null;

        return Logging.getMessage("DataStoreProducer.InvalidDataStoreParamters", sb.toString());
    }

    protected java.io.File installLocationFor(AVList params)
    {
        String fileStoreLocation = params.getStringValue(AVKey.FILE_STORE_LOCATION);
        String dataCacheName = params.getStringValue(AVKey.DATA_CACHE_NAME);
        if (fileStoreLocation == null || dataCacheName == null)
            return null;

        String path = appendPathPart(fileStoreLocation, dataCacheName);
        if (path == null || path.length() < 1)
            return null;

        return new java.io.File(path);
    }

    //**************************************************************//
    //********************  LevelSet Assembly  *********************//
    //**************************************************************//

    protected void initProductionParameters(AVList params)
    {
        // Used by subclasses to specify default production parameters.
    }

    protected void initLevelSetParameters(AVList params)
    {
        int largeThreshold = Configuration.getIntegerValue(AVKey.TILED_RASTER_PRODUCER_LARGE_DATASET_THRESHOLD,
            DEFAULT_TILED_RASTER_PRODUCER_LARGE_DATASET_THRESHOLD);
        boolean isDataSetLarge = this.isDataSetLarge(this.dataRasterList, largeThreshold);

        Sector sector = (Sector) params.getValue(AVKey.SECTOR);
        if (sector == null)
        {
            // Compute a sector that bounds the data rasters. Make sure the sector does not exceed the limits of
            // latitude and longitude.
            sector = this.computeBoundingSector(this.dataRasterList);
            if (sector != null)
                sector = sector.intersection(Sector.FULL_SPHERE);
            params.setValue(AVKey.SECTOR, sector);
        }

        Integer tileWidth = (Integer) params.getValue(AVKey.TILE_WIDTH);
        if (tileWidth == null)
        {
            tileWidth = isDataSetLarge ? DEFAULT_TILE_WIDTH_AND_HEIGHT : DEFAULT_SINGLE_LEVEL_TILE_WIDTH_AND_HEIGHT;
            params.setValue(AVKey.TILE_WIDTH, tileWidth);
        }

        Integer tileHeight = (Integer) params.getValue(AVKey.TILE_HEIGHT);
        if (tileHeight == null)
        {
            tileHeight = isDataSetLarge ? DEFAULT_TILE_WIDTH_AND_HEIGHT : DEFAULT_SINGLE_LEVEL_TILE_WIDTH_AND_HEIGHT;
            params.setValue(AVKey.TILE_HEIGHT, tileHeight);
        }

        LatLon rasterTileDelta = this.computeRasterTileDelta(tileWidth, tileHeight, this.dataRasterList);
        LatLon desiredLevelZeroDelta = this.computeDesiredTileDelta(sector);

        Integer numLevels = (Integer) params.getValue(AVKey.NUM_LEVELS);
        if (numLevels == null)
        {
            // If the data set is large, then use compute a number of levels for the full pyramid. Otherwise use a
            // single level.
            numLevels = isDataSetLarge ? this.computeNumLevels(desiredLevelZeroDelta, rasterTileDelta) : 1;
            params.setValue(AVKey.NUM_LEVELS, numLevels);
        }

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

        LatLon levelZeroTileDelta = (LatLon) params.getValue(AVKey.LEVEL_ZERO_TILE_DELTA);
        if (levelZeroTileDelta == null)
        {
            double scale = Math.pow(2d, numLevels - 1);
            levelZeroTileDelta = LatLon.fromDegrees(
                scale * rasterTileDelta.getLatitude().degrees,
                scale * rasterTileDelta.getLongitude().degrees);
            params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, levelZeroTileDelta);
        }

        LatLon tileOrigin = (LatLon) params.getValue(AVKey.TILE_ORIGIN);
        if (tileOrigin == null)
        {
            tileOrigin = new LatLon(sector.getMinLatitude(), sector.getMinLongitude());
            params.setValue(AVKey.TILE_ORIGIN, tileOrigin);
        }

        // If the default or caller-specified values define a level set that does not fit in the limits of latitude
        // and longitude, then we re-define the level set parameters using values known to fit in those limits.
        if (!this.isWithinLatLonLimits(sector, levelZeroTileDelta, tileOrigin))
        {
            String message = "TiledRasterProducer: native tiling is outside lat/lon limits. Falling back to default tiling.";
            Logging.logger().warning(message);

            levelZeroTileDelta = LatLon.fromDegrees(DEFAULT_LEVEL_ZERO_TILE_DELTA, DEFAULT_LEVEL_ZERO_TILE_DELTA);
            params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, levelZeroTileDelta);

            tileOrigin = new LatLon(Angle.NEG90, Angle.NEG180);
            params.setValue(AVKey.TILE_ORIGIN, tileOrigin);
            
            numLevels = this.computeNumLevels(levelZeroTileDelta, rasterTileDelta);
            params.setValue(AVKey.NUM_LEVELS, numLevels);

            int numLevelsNeeded = isDataSetLarge ? this.computeNumLevels(desiredLevelZeroDelta, rasterTileDelta) : 1;
            numEmptyLevels = (numLevels > numLevelsNeeded) ? (numLevels - numLevelsNeeded) : 0;
            params.setValue(AVKey.NUM_EMPTY_LEVELS, numEmptyLevels);
        }
    }

    protected boolean isDataSetLarge(Iterable<? extends DataRaster> rasters, int largeThreshold)
    {
        Sector sector = this.computeBoundingSector(rasters);
        LatLon pixelSize = this.computeSmallestPixelSize(rasters);
        int sectorWidth = (int) Math.ceil(sector.getDeltaLonDegrees() / pixelSize.getLongitude().degrees);
        int sectorHeight = (int) Math.ceil(sector.getDeltaLatDegrees() / pixelSize.getLatitude().degrees);
        return (sectorWidth >= largeThreshold) || (sectorHeight >= largeThreshold);
    }

    protected boolean isWithinLatLonLimits(Sector sector, LatLon tileDelta, LatLon tileOrigin)
    {
        double minLat = Math.floor((sector.getMinLatitude().degrees - tileOrigin.getLatitude().degrees)
            / tileDelta.getLatitude().degrees);
        minLat = tileOrigin.getLatitude().degrees + minLat * tileDelta.getLatitude().degrees;
        double maxLat = Math.ceil((sector.getMaxLatitude().degrees - tileOrigin.getLatitude().degrees)
            / tileDelta.getLatitude().degrees);
        maxLat = tileOrigin.getLatitude().degrees + maxLat * tileDelta.getLatitude().degrees;
        double minLon = Math.floor((sector.getMinLongitude().degrees - tileOrigin.getLongitude().degrees)
            / tileDelta.getLongitude().degrees);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?