在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;
}
}