worldwind java批量下载Mercator数据

在Worldwind java的示例代码中,有一个示例是BulkDownload,它的主要功能就是下载可批量下载图层的数据。具体过程可以自行分析BulkDownload的源码,源码比较简单,这里不进行分析。
可批量下载图层的标志是图层是否实现了BulkRetrievable接口,如果实现了这个接口,就可以批量对该图层的数据进行批量下载。

在gov.nasa.worldwind.retrieve包中,BulkRetrievable的定义如下:

public interface BulkRetrievable
{
    BulkRetrievalThread makeLocal(Sector sector, double resolution, BulkRetrievalListener listener);
    long getEstimatedMissingDataSize(Sector sector, double resolution);
    long getEstimatedMissingDataSize(Sector sector, double resolution, FileStore fileStore);
    BulkRetrievalThread makeLocal(Sector sector, double resolution, FileStore fileStore,
        BulkRetrievalListener listener);
    String getName();
}

它主要有两个功能,一是评估选定区域在指定可能需要下载的数据量;二是生成一个下载的线程。

为了实现对TiledImageLayer的批量下载,worldwind java的源码中给出了示例,一是针对BasicTiledImageLayer,实现BulkRetrievable接口;二是从BulkRetrievalThread建立派生类BasicTiledImageLayerBulkDownloader,以实现tile图像的下载。

在实现了Mercator层数据加载的情况下,为了进一步实现对Mercator层数据的下载,可以仿照BasicTiledImageLayer的做法,先实现BulkRetrievable接口,后建立对应的下载线程类。

  • 针对Mecator图层实现BulkRetrievable接口

从TileImageLayer中把对BulkRetrievable实现的函数复制过来,然后进行修改。
需要注意的是BasicMercatorTiledImageLayer层与BasicTiledImageLayer层不同的地方主要是Sector不同,在BasicMercatorTiledImageLayer中几乎所有进行计算的Sector均是MercatorSector,因此,只需要将代码中的计算Sector改成计算MercatorSector即可完成。

Sector的计算如下:

Sector s = Sector.fromDegrees(
    sector.getMinLatitude().degrees + dLat * row,
    sector.getMinLatitude().degrees + dLat * row + dLat,
    sector.getMinLongitude().degrees + dLon * col,
    sector.getMinLongitude().degrees + dLon * col + dLon);

MercatorSector的计算如下:

MercatorSector s = MercatorSector.fromDegrees(
        l2 + dLat * row,
        l2 + dLat * row + dLat,
        sector.getMinLongitude().degrees + dLon * col,
        sector.getMinLongitude().degrees + dLon * col + dLon);

BasicTiledImageLayer层中,每一个Sector将由许多的Tile组成,每一个Tile由TextureTile对象表示,相应地,在BasicMercatorTiledImageLayer层中,对应的Sector变成了MecatorSector,Tile也变成了MercatorTextureTile,

sectorTiles[nwRow - row][col - nwCol] = new MercatorTextureTile(
        mSector, targetLevel, row, col);

Worldwind java的源码中只针对BasicTiledImageLayer实现了BulkRetrievable接口,而用来载入基于Mercator投影的图层并没有实现这一接口。

BasicMercatorTiledImageLayer的扩展类示例如下:

public class BasicMercatorTiledImageLayerCustom extends BasicMercatorTiledImageLayer implements BulkRetrievable
{
    private String strLayerName = "";
    public BasicMercatorTiledImageLayerCustom(TileUrlBuilder ub,String labelName)
    {
        super(makeLevels(ub,labelName,labelName));
    }

    public BasicMercatorTiledImageLayerCustom(TileUrlBuilder ub,String labelName,String datasetName)
    {
        super(makeLevels(ub,labelName,datasetName));
        strLayerName = labelName;
    }

    private static LevelSet makeLevels(TileUrlBuilder ub,String labelName,String datasetName)
    {
        return makeLevels(ub,labelName,datasetName,".png","www.test.com",,0,16);
    }

    private static LevelSet makeLevels(TileUrlBuilder ub,String labelName,String datasetName,String suffix, String serverName,int numLevelMin,int numLevelMax)
    {
        return makeLevels(ub,labelName,datasetName,suffix,serverName,numLevelMin,numLevelMax,256,256);
    }

    private static LevelSet makeLevels(TileUrlBuilder ub, String labelName,String datasetName,String suffix,String serverName,int numLevelMin,int numLevelMax,int tileWidth,int tileHeight)
    {
        AVList params = new AVListImpl();

        String strTemp = "";

        params.setValue(AVKey.TILE_WIDTH, tileWidth);        
        params.setValue(AVKey.TILE_HEIGHT, tileHeight);
        params.setValue(AVKey.DATA_CACHE_NAME, "Earth/"+labelName);
        params.setValue(AVKey.SERVICE, serverName);
        params.setValue(AVKey.DATASET_NAME, datasetName);
        params.setValue(AVKey.FORMAT_SUFFIX, suffix);
        params.setValue(AVKey.NUM_LEVELS, numLevelMax);        
        params.setValue(AVKey.NUM_EMPTY_LEVELS, numLevelMin);
        params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(Angle.fromDegrees(22.5d), Angle.fromDegrees(45d)));
        params.setValue(AVKey.SECTOR, new MercatorSector(-1.0, 1.0, Angle.NEG180, Angle.POS180));
        params.setValue(AVKey.TILE_URL_BUILDER, ub);

        return new LevelSet(params);
    }

    protected boolean transformAndSave(BufferedImage image, MercatorSector sector,File outFile)
    {
        image = transform(image, sector);
        String extension = outFile.getName().substring(
                outFile.getName().lastIndexOf('.') + 1);
        synchronized (this.fileLock) // synchronized with read of file in RequestTask.run()
        {
            //return ImageIO.write(image, extension, outFile);
            return ImageUtilCustom.savePngTransparent(image, extension, outFile);
        }
    }

    @Override
    public String toString()
    {
        return strLayerName;
    }



    ////////////////////////////////////////////////////////////////

    protected void retrieveTexture(MercatorTextureTile 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(MercatorTextureTile 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(MercatorTextureTile 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);
            postProcessor = new DownloadPostProcessor(tile,this);
        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());
    }

    //////////////////////////////////////////////////////////////


    @Override
    public BulkRetrievalThread makeLocal(Sector sector, double resolution, BulkRetrievalListener listener) {
        return makeLocal(sector, resolution, null, listener);
    }

    @Override
    public long getEstimatedMissingDataSize(Sector sector, double resolution) {
        return 0;
    }

    @Override
    public long getEstimatedMissingDataSize(Sector sector, double resolution, FileStore fileStore) {
        return 0;
    }

    @Override
    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;

        BasicMercatorTiledImageLayerCustomBulkDownloader thread = new BasicMercatorTiledImageLayerCustomBulkDownloader(this, targetSector,
                resolution, fileStore != null ? fileStore : this.getDataFileStore(), listener);
        thread.setDaemon(true);
        thread.start();
        return thread;
    }

    public int countImagesInSector(Sector sector, int levelNumber)
    {
        MercatorSector s = MercatorSector.fromSector(sector);
        return countImagesInSector(s,levelNumber);
    }

    public int countImagesInSector(MercatorSector sector, int levelNumber)
    {
        ArrayList<Integer> li = GetRect(sector,levelNumber);

        if(li == null)
            return  0;

        int seRow = li.get(2);
        int nwRow = li.get(3);
        int nwCol = li.get(1);
        int seCol = li.get(0);

        int numRows = nwRow - seRow + 1;
        int numCols = seCol - nwCol + 1;

        return numRows * numCols;
    }

    public MercatorTextureTile[][] getTilesInSector(MercatorSector sector,
                                                    int levelNumber)
    {
        if (sector == null)
        {
            String msg = Logging.getMessage("nullValue.SectorIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        Level targetLevel = this.levels.getLastLevel();
        if (levelNumber >= 0)
        {
            for (int i = levelNumber; i < this.getLevels().getLastLevel()
                    .getLevelNumber(); i++)
            {
                if (this.levels.isLevelEmpty(i))
                    continue;

                targetLevel = this.levels.getLevel(i);
                break;
            }
        }

        // Collect all the tiles intersecting the input sector.
        ArrayList<Integer> li = GetRect(sector,levelNumber);

        int seRow = li.get(2);
        int nwRow = li.get(3);
        int nwCol = li.get(1);
        int seCol = li.get(0);

        Angle lonOrigin = this.levels.getTileOrigin().getLongitude();
        double dLat = targetLevel.getTileDelta().getLatitude().degrees/90;
        double dLon = targetLevel.getTileDelta().getLongitude().degrees;

        int numRows = nwRow - seRow + 1;
        int numCols = seCol - nwCol + 1;

        MercatorTextureTile[][] sectorTiles = new MercatorTextureTile[numRows][numCols];

        for (int row = nwRow; row >= seRow; row--)
        {
            for (int col = nwCol; col <= seCol; col++)
            {
                MercatorSector mSector = new MercatorSector(
                        -1 + dLat * row,
                        -1 + dLat * row + dLat,
                        lonOrigin.addDegrees( dLon * col ),
                        lonOrigin.addDegrees( dLon * col + dLon));
                sectorTiles[nwRow - row][col - nwCol] = new MercatorTextureTile(
                        mSector, targetLevel, row, col);
            }
        }

        return sectorTiles;
    }

    public ArrayList<Integer> GetRect(Sector sector,int levelNumber)
    {
        if (sector == null)
        {
            return null;
        }

        Level targetLevel = this.levels.getLastLevel();
        if (levelNumber >= 0)
        {
            for (int i = levelNumber; i < this.getLevels().getLastLevel()
                    .getLevelNumber(); i++)
            {
                if (this.levels.isLevelEmpty(i))
                    continue;

                targetLevel = this.levels.getLevel(i);
                break;
            }
        }

        ArrayList<Integer> li = new ArrayList<Integer>();

        LatLon delta = targetLevel.getTileDelta();
        Angle latOrigin = this.levels.getTileOrigin().getLatitude();
        Angle lonOrigin = this.levels.getTileOrigin().getLongitude();

        double dLatMin = MercatorSector.gudermannianInverse(sector.getMinLatitude());
        double dLatMax = MercatorSector.gudermannianInverse(sector.getMaxLatitude());

        double dLat = delta.getLatitude().degrees/90;
        double dLon = delta.getLongitude().degrees;

        int seRow = (int) ((dLatMin + 1) / dLat);
        int nwRow = (int) ((dLatMax + 1) / dLat);
        int nwCol = Tile.computeColumn(delta.getLongitude(), sector
                .getMinLongitude(), lonOrigin);
        int seCol = Tile.computeColumn(delta.getLongitude(), sector
                .getMaxLongitude(), lonOrigin);

        li.add(seCol);
        li.add(nwCol);

        li.add(seRow);
        li.add(nwRow);

        return li;
    }
}
  • 实现Mercator图层的BulkRetrievalThread接口

仿照BasicTiledImageLayerBulkDownloader建立BasicMercatorTiledImageLayerCustomBulkDownloader,示例如下:

public class BasicMercatorTiledImageLayerCustomBulkDownloader extends BulkRetrievalThread
{
    protected final static int MAX_TILE_COUNT_PER_REGION = 200;
    protected final static long DEFAULT_AVERAGE_FILE_SIZE = 350000L;

    protected final BasicMercatorTiledImageLayerCustom layer;
    protected final int level;
    protected ArrayList<MercatorTextureTile> missingTiles;


    public BasicMercatorTiledImageLayerCustomBulkDownloader(BasicMercatorTiledImageLayerCustom layer, Sector sector, double resolution,
                                                        BulkRetrievalListener listener)
    {               this(layer,sector,resolution,layer.getDataFileStore(),listener);
    }


    public BasicMercatorTiledImageLayerCustomBulkDownloader(BasicMercatorTiledImageLayerCustom layer, Sector sector, double resolution,
                                                        FileStore fileStore, BulkRetrievalListener listener)
    {
        // Arguments checked in parent constructor
        super(layer, sector, resolution, fileStore, listener);

        this.layer = layer;
        this.level = this.layer.computeLevelForResolution(sector, resolution);
    }

    public void run()
    {
        try
        {
            // Init progress with missing tile count estimate
            this.progress.setTotalCount(this.estimateMissingTilesCount(50));
            this.progress.setTotalSize(this.progress.getTotalCount() * estimateAverageTileSize());

            // Determine and request missing tiles by level/region
            for (int levelNumber = 0; levelNumber <= this.level; levelNumber++)
            {
                if (this.layer.getLevels().isLevelEmpty(levelNumber))
                    continue;

                int div = this.computeRegionDivisions(this.sector, levelNumber, MAX_TILE_COUNT_PER_REGION);
                Iterator<MercatorSector> regionsIterator = this.getRegionIterator(this.sector, div);

                MercatorSector region;
                while (regionsIterator.hasNext())
                {
                    region = regionsIterator.next();
                    // Determine missing tiles
                    this.missingTiles = getMissingTilesInSector(region, levelNumber);

                    // Submit missing tiles requests at intervals
                    while (this.missingTiles.size() > 0)
                    {
                        submitMissingTilesRequests();
                        if (this.missingTiles.size() > 0)
                            Thread.sleep(RETRIEVAL_SERVICE_POLL_DELAY);
                    }
                }
            }
            // Set progress to 100%
            this.progress.setTotalCount(this.progress.getCurrentCount());
            this.progress.setTotalSize(this.progress.getCurrentSize());
        }
        catch (InterruptedException e)
        {
            String message = Logging.getMessage("generic.BulkRetrievalInterrupted", this.layer.getName());
            Logging.logger().log(java.util.logging.Level.WARNING, message, e);
        }
        catch (Exception e)
        {
            String message = Logging.getMessage("generic.ExceptionDuringBulkRetrieval", this.layer.getName());
            Logging.logger().severe(message);
            throw new RuntimeException(message);
        }
    }


    protected synchronized void submitMissingTilesRequests() throws InterruptedException
    {
        RetrievalService rs = WorldWind.getRetrievalService();
        int i = 0;
        while (this.missingTiles.size() > i && rs.isAvailable())
        {
            Thread.sleep(1); // generates InterruptedException if thread has been interrupted

            MercatorTextureTile tile = this.missingTiles.get(i);

            if (this.layer.getLevels().isResourceAbsent(tile))
            {
                removeAbsentTile(tile);  // tile is absent, count it off.
                continue;
            }

            URL url = this.fileStore.findFile(tile.getPath(), false);
            if (url != null)
            {
                // tile has been retrieved and is local now, count it as retrieved.
                removeRetrievedTile(tile);
                continue;
            }

            this.layer.retrieveTexture(tile, createBulkDownloadPostProcessor(tile));
            //this.layer.downloadTexture(tile);
            i++;
        }
    }

    protected BasicMercatorTiledImageLayer.DownloadPostProcessor createBulkDownloadPostProcessor(MercatorTextureTile tile)
    {
        return new BulkDownloadPostProcessor(tile, this.layer, this.fileStore);
    }

    protected class BulkDownloadPostProcessor extends BasicMercatorTiledImageLayer.DownloadPostProcessor
    {
        public BulkDownloadPostProcessor(MercatorTextureTile tile, BasicMercatorTiledImageLayer layer, FileStore fileStore)
        {
            super(tile, layer, fileStore);
        }

        public ByteBuffer run(Retriever retriever) {

            ByteBuffer buffer = super.run(retriever);

            if (retriever.getState().equals(Retriever.RETRIEVER_STATE_SUCCESSFUL))
                removeRetrievedTile(this.tile);

            try {
                if (hasRetrievalListeners())
                    callRetrievalListeners(retriever, this.tile);

            } catch (Exception e)
            {
                System.console().printf(e.getMessage());
            }
            return buffer;
        }
    }

    protected void callRetrievalListeners(Retriever retriever, TextureTile tile) throws MalformedURLException {
        String eventType = (retriever.getState().equals(Retriever.RETRIEVER_STATE_SUCCESSFUL))
            ? BulkRetrievalEvent.RETRIEVAL_SUCCEEDED : BulkRetrievalEvent.RETRIEVAL_FAILED;
        String strPre="";
        if(retriever instanceof URLRetriever)
            strPre = ((URLRetriever) retriever).getUrl().toString()+"\t";
        super.callRetrievalListeners(new BulkRetrievalEvent(this.layer, eventType,  strPre+tile.getPath()));

    }

    protected synchronized void removeRetrievedTile(MercatorTextureTile tile)
    {
        this.missingTiles.remove(tile);
        // Update progress
        this.progress.setCurrentCount(this.progress.getCurrentCount() + 1);
        this.progress.setCurrentSize(this.progress.getCurrentSize() + estimateAverageTileSize());
        this.progress.setLastUpdateTime(System.currentTimeMillis());
        this.normalizeProgress();
    }

    protected synchronized void removeAbsentTile(TextureTile tile)
    {
        this.missingTiles.remove(tile);
        // Decrease progress expected total count and size
        this.progress.setTotalCount(this.progress.getTotalCount() - 1);
        this.progress.setTotalSize(this.progress.getTotalSize() - estimateAverageTileSize());
        this.progress.setLastUpdateTime(System.currentTimeMillis());
        this.normalizeProgress();
    }

    protected void normalizeProgress()
    {
        if (this.progress.getTotalCount() < this.progress.getCurrentCount())
        {
            this.progress.setTotalCount(this.progress.getCurrentCount());
            this.progress.setTotalSize(this.progress.getCurrentSize());
        }
    }


    protected long getEstimatedMissingDataSize()
    {
        // Get missing tiles count estimate
        long totMissing = estimateMissingTilesCount(6);
        // Get average tile size estimate
        long averageTileSize = estimateAverageTileSize();

        return totMissing * averageTileSize;
    }

    protected long estimateMissingTilesCount(int numSamples)
    {
        int maxLevel = this.layer.computeLevelForResolution(this.sector, this.resolution);
        // Total expected tiles
        long totCount = 0;
        for (int levelNumber = 0; levelNumber <= maxLevel; levelNumber++)
        {
            if (!this.layer.getLevels().isLevelEmpty(levelNumber))
                totCount += this.layer.countImagesInSector(sector, levelNumber);
        }
        // Sample random small sized sectors at finest level
        int div = this.computeRegionDivisions(this.sector, maxLevel, 36); // max 6x6 tiles per region
        MercatorSector[] regions = computeRandomRegions(this.sector, div, numSamples);
        long regionMissing = 0;
        long regionCount = 0;
        try
        {
            if (regions.length < numSamples)
            {
                regionCount = this.layer.countImagesInSector(this.sector, maxLevel);
                regionMissing = getMissingTilesInSector(MercatorSector.fromSector(this.sector), maxLevel).size();
            }
            else
            {
                for (MercatorSector region : regions)
                {
                    // Count how many tiles are missing in each sample region
                    regionCount += this.layer.countImagesInSector(region, maxLevel);
                    regionMissing += getMissingTilesInSector(region, maxLevel).size();
                }
            }
        }
        catch (InterruptedException e)
        {
            return 0;
        }
        catch (Exception e)
        {
            String message = Logging.getMessage("generic.ExceptionDuringDataSizeEstimate", this.layer.getName());
            Logging.logger().severe(message);
            throw new RuntimeException(message);
        }

        // Extrapolate total missing count
        return (long)(totCount * ((double)regionMissing / regionCount));
    }

    protected int computeRegionDivisions(Sector sector, int levelNumber, int maxCount)
    {
        long tileCount = this.layer.countImagesInSector(sector, levelNumber);

        if (tileCount <= maxCount)
            return 1;

        // Divide sector in regions that will contain no more tiles then maxCount
        return (int) Math.ceil(Math.sqrt((double) tileCount / maxCount));
    }

    public MercatorSector[] subdivide(Sector sector, int div)
    {
        double l1 = MercatorSector.gudermannianInverse(sector.getMaxLatitude());
        double l2 = MercatorSector.gudermannianInverse(sector.getMinLatitude());
        //final double dLat = sector.getDeltaLat().degrees / div;
        final double dLat = (l1-l2) / div;
        double dLon = sector.getDeltaLon().degrees / div;

        MercatorSector[] sectors = new MercatorSector[div * div];
        int idx = 0;
        for (int row = 0; row < div; row++)
        {
            for (int col = 0; col < div; col++)
            {
                sectors[idx++] = MercatorSector.fromDegrees(
                        l2 + dLat * row,
                        l2 + dLat * row + dLat,
                        sector.getMinLongitude().degrees + dLon * col,
                        sector.getMaxLongitude().degrees + dLon * col + dLon);
            }
        }

        return sectors;
    }

    protected MercatorSector[] computeRandomRegions(Sector sector, int div, int numRegions)
    {
        if (numRegions > div * div)
            return subdivide(sector,div);

        double l1 = MercatorSector.gudermannianInverse(sector.getMaxLatitude());
        double l2 = MercatorSector.gudermannianInverse(sector.getMinLatitude());
        //final double dLat = sector.getDeltaLat().degrees / div;
        final double dLat = (l1-l2) / div;
        final double dLon = sector.getDeltaLon().degrees / div;
        ArrayList<MercatorSector> regions = new ArrayList<MercatorSector>(numRegions);
        Random rand = new Random();
        while (regions.size() < numRegions)
        {
            int row = rand.nextInt(div);
            int col = rand.nextInt(div);

            MercatorSector s = MercatorSector.fromDegrees(
                    l2 + dLat * row,
                    l2 + dLat * row + dLat,
                    sector.getMinLongitude().degrees + dLon * col,
                    sector.getMinLongitude().degrees + dLon * col + dLon);


            if (!regions.contains(s))
                regions.add(s);
        }

        return regions.toArray(new MercatorSector[numRegions]);
    }

    protected Iterator<MercatorSector> getRegionIterator(final Sector sector, final int div)
    {
        double l1 = MercatorSector.gudermannianInverse(sector.getMaxLatitude());
        double l2 = MercatorSector.gudermannianInverse(sector.getMinLatitude());
        //final double dLat = sector.getDeltaLat().degrees / div;
        final double dLat = (l1-l2) / div;
        final double dLon = sector.getDeltaLon().degrees / div;

        return new Iterator<MercatorSector>()
        {
            int row = 0;
            int col = 0;

            public boolean hasNext()
            {
                return row < div;
            }

            public MercatorSector next()
            {
                double l2 = MercatorSector.gudermannianInverse(sector.getMinLatitude());

                MercatorSector s = MercatorSector.fromDegrees(
                        l2 + dLat * row,
                        l2 + dLat * row + dLat,
                        sector.getMinLongitude().degrees + dLon * col,
                        sector.getMinLongitude().degrees + dLon * col + dLon);

                col++;
                if (col >= div)
                {
                    col = 0;
                    row++;
                }
                return s;
            }

            public void remove()
            {

            }
        };
    }

    protected ArrayList<MercatorTextureTile> getMissingTilesInSector(MercatorSector sector, int levelNumber)
        throws InterruptedException
    {
        ArrayList<MercatorTextureTile> tiles = new ArrayList<MercatorTextureTile>();

        MercatorTextureTile[][] tileArray = this.layer.getTilesInSector((sector), levelNumber);
        for (MercatorTextureTile[] row : tileArray)
        {
            for (MercatorTextureTile tile : row)
            {
                Thread.sleep(1); // generates InterruptedException if thread has been interrupted

                if (tile == null)
                    continue;

                if (isTileLocalOrAbsent(tile))
                    continue;  // tile is local or absent

                tiles.add(tile);
            }
        }
        return tiles;
    }

    protected boolean isTileLocalOrAbsent(MercatorTextureTile tile)
    {
        if (this.layer.getLevels().isResourceAbsent(tile))
            return true;  // tile is absent

        URL url = this.fileStore.findFile(tile.getPath(), false);

        //return url != null && !this.layer.isTextureFileExpired(tile, url, fileStore);
        return url != null && !this.layer.isTextureExpired(tile, url);
    }

    protected long estimateAverageTileSize()
    {
        Long previouslyComputedSize = (Long) this.layer.getValue(AVKey.AVERAGE_TILE_SIZE);
        if (previouslyComputedSize != null)
            return previouslyComputedSize;

        long size = 0;
        long count = 0;

        // Average cached tile files size in a few directories from first non empty level
        Level targetLevel = this.layer.getLevels().getFirstLevel();
        while (targetLevel.isEmpty() && !targetLevel.equals(this.layer.getLevels().getLastLevel()))
        {
            targetLevel = this.layer.getLevels().getLevel(targetLevel.getLevelNumber() + 1);
        }

        File cacheRoot = new File(this.fileStore.getWriteLocation(), targetLevel.getPath());
        if (cacheRoot.exists())
        {
            File[] rowDirs = cacheRoot.listFiles(new FileFilter()
            {
                public boolean accept(File file)
                {
                    return file.isDirectory();
                }
            });
            for (File dir : rowDirs)
            {
                long averageSize = computeAverageTileSize(dir);
                if (averageSize > 0)
                {
                    size += averageSize;
                    count++;
                }
                if (count >= 2) // average content from up to 2 cache folders
                    break;
            }
        }

        Long averageTileSize = DEFAULT_AVERAGE_FILE_SIZE;
        if (count > 0 && size > 0)
        {
            averageTileSize = size / count;
            this.layer.setValue(AVKey.AVERAGE_TILE_SIZE, averageTileSize);
        }

        return averageTileSize;
    }

    protected long computeAverageTileSize(File dir)
    {
        long size = 0;
        int count = 0;

        File[] files = dir.listFiles();
        for (File file : files)
        {
            try
            {
                FileInputStream fis = new FileInputStream(file);
                size += fis.available();
                fis.close();
                count++;
            }
            catch (IOException e)
            {
                count += 0;
            }
        }

        return count > 0 ? size / count : 0;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值