worldwind java批量下载Mercator数据

来源:互联网 发布:网络流行文化的论文 编辑:程序博客网 时间:2024/05/15 20:33

在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 0