75 |
75 |
|
76 |
76 |
/**
|
77 |
77 |
* Represents a tiled image
|
|
78 |
*
|
78 |
79 |
* @author dmartinezizquierdo
|
79 |
80 |
*
|
80 |
81 |
*/
|
... | ... | |
88 |
89 |
private FilesystemServerExplorerProviderFactory factory;
|
89 |
90 |
private String providerName;
|
90 |
91 |
private String extension;
|
91 |
|
private SortedMap<Integer,Double> pixelSizePerZoomLevel;
|
92 |
|
// private SortedMap<Integer,Envelope> envelopePerZoomLevel;
|
|
92 |
private SortedMap<Integer, Double> pixelSizePerZoomLevel;
|
|
93 |
// private SortedMap<Integer,Envelope> envelopePerZoomLevel;
|
93 |
94 |
private File tilesFolder;
|
94 |
95 |
private Map<String, Tile> recentAccededTiles;
|
95 |
96 |
|
96 |
|
private Integer bandNumber=null;
|
|
97 |
private Integer bandNumber = null;
|
97 |
98 |
|
98 |
99 |
private int[] dataTypes;
|
99 |
100 |
|
100 |
101 |
private IProjection crs;
|
101 |
|
// Este envelope sirve para calcular el nombre de la fila y columna del tile en la estructura de cach?.
|
102 |
102 |
private Envelope structExtent;
|
103 |
103 |
private Object colorInterpretation;
|
104 |
104 |
private Object legend;
|
... | ... | |
119 |
119 |
this.query = query;
|
120 |
120 |
GeometryManager geoManager = GeometryLocator.getGeometryManager();
|
121 |
121 |
|
122 |
|
// envelopePerZoomLevel = new TreeMap<Integer, Envelope>();
|
|
122 |
// envelopePerZoomLevel = new TreeMap<Integer, Envelope>();
|
123 |
123 |
pixelSizePerZoomLevel = new TreeMap<Integer, Double>();
|
124 |
124 |
recentAccededTiles = new HashMap<String, Tile>();
|
125 |
125 |
|
126 |
126 |
// FIXME
|
127 |
127 |
File tilePropertiesFile = new File(folder, "tileCacheStruct.xml");
|
128 |
128 |
tilesFolder = new File(folder, "V" + File.separatorChar + "Z" + File.separatorChar + "T");
|
129 |
|
if(!tilesFolder.exists()){
|
|
129 |
if (!tilesFolder.exists()) {
|
130 |
130 |
tilesFolder.mkdirs();
|
131 |
131 |
}
|
132 |
132 |
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
... | ... | |
199 |
199 |
|
200 |
200 |
pixelSizePerZoomLevel.put(zoomLevel, pixelSize);
|
201 |
201 |
|
202 |
|
// NodeList extentList = level.getElementsByTagName("extent");
|
203 |
|
// if (extentList.getLength() > 0) {
|
204 |
|
// Element extentNode = (Element) extentList.item(0);
|
205 |
|
// if (extentNode != null) {
|
206 |
|
// Double minX =
|
207 |
|
// Double.valueOf(extentNode.getElementsByTagName("minX").item(0).getTextContent());
|
208 |
|
// Double minY =
|
209 |
|
// Double.valueOf(extentNode.getElementsByTagName("minY").item(0).getTextContent());
|
210 |
|
// Double maxX =
|
211 |
|
// Double.valueOf(extentNode.getElementsByTagName("maxX").item(0).getTextContent());
|
212 |
|
// Double maxY =
|
213 |
|
// Double.valueOf(extentNode.getElementsByTagName("maxY").item(0).getTextContent());
|
214 |
|
// Envelope extent = geoManager.createEnvelope(minX, minY, maxX, maxY, SUBTYPES.GEOM2D);
|
215 |
|
// envelopePerZoomLevel.put(zoomLevel, extent);
|
216 |
|
// }
|
217 |
|
// }
|
218 |
202 |
}
|
219 |
203 |
}
|
220 |
204 |
}
|
... | ... | |
222 |
206 |
throw new RuntimeException();
|
223 |
207 |
}
|
224 |
208 |
}
|
225 |
|
//
|
226 |
|
// public TileStructImage(File folder, IProjection crs, RasterStoreProvider innerProvider) {
|
227 |
|
//
|
228 |
|
// this.innerProvider = innerProvider;
|
229 |
|
// this.crs = crs;
|
230 |
|
//
|
231 |
|
//
|
232 |
|
//
|
233 |
|
// }
|
234 |
209 |
|
235 |
|
|
236 |
210 |
/**
|
237 |
211 |
* @param pixelSize
|
238 |
212 |
* @return The appropiate zoom level for the pizelSize
|
239 |
213 |
*/
|
240 |
|
public int getZoomLvlForPixelSize(double pixelSize){
|
241 |
|
int zoom=0;
|
242 |
|
for (int i:pixelSizePerZoomLevel.keySet()){
|
243 |
|
zoom=i;
|
244 |
|
double levelPixelSize=pixelSizePerZoomLevel.get(zoom);
|
245 |
|
if (pixelSize>=levelPixelSize){
|
|
214 |
public int getZoomLvlForPixelSize(double pixelSize) {
|
|
215 |
int zoom = 0;
|
|
216 |
for (int i : pixelSizePerZoomLevel.keySet()) {
|
|
217 |
zoom = i;
|
|
218 |
double levelPixelSize = pixelSizePerZoomLevel.get(zoom);
|
|
219 |
if (pixelSize >= levelPixelSize) {
|
246 |
220 |
return zoom;
|
247 |
221 |
}
|
248 |
222 |
}
|
249 |
223 |
return zoom;
|
250 |
224 |
}
|
251 |
225 |
|
252 |
|
private int[] calculateStructRowCol(int zoomLevel, int tileRow, int tileCol){
|
253 |
|
//index 0 structRow, index 1 structCol
|
254 |
|
int[] structRowCol = new int[2];
|
255 |
|
|
256 |
|
// if(envelopePerZoomLevel==null || envelopePerZoomLevel.isEmpty()){
|
257 |
|
structRowCol[0] = tileRow;
|
258 |
|
structRowCol[1] = tileCol;
|
259 |
|
// } else {
|
260 |
|
// Envelope envelopeInZoomLevel = envelopePerZoomLevel.get(zoomLevel);
|
261 |
|
// structRowCol[0] = (int)(-(envelopeInZoomLevel.getMaximum(DIMENSIONS.Y)-structExtent.getMaximum(DIMENSIONS.Y))/(pixelSizePerZoomLevel.get(zoomLevel)*rowsPerTile))+tileRow;
|
262 |
|
// structRowCol[1] = (int)((envelopeInZoomLevel.getMinimum(DIMENSIONS.X)-structExtent.getMinimum(DIMENSIONS.X))/(pixelSizePerZoomLevel.get(zoomLevel)*columnsPerTile))+tileCol;
|
263 |
|
// }
|
264 |
|
|
265 |
|
return structRowCol;
|
266 |
|
|
267 |
|
}
|
268 |
|
|
269 |
226 |
/**
|
270 |
227 |
* @param buffer
|
271 |
228 |
* @param band
|
... | ... | |
277 |
234 |
* @throws CloneNotSupportedException
|
278 |
235 |
* @throws ValidateDataParametersException
|
279 |
236 |
*/
|
280 |
|
public Band fetchTile(int band, int zoomLevel, int tileRow, int tileCol) throws CreateEnvelopeException, ValidateDataParametersException, CloneNotSupportedException {
|
|
237 |
public Band fetchTile(int band, int zoomLevel, int structRow, int structCol) throws CreateEnvelopeException,
|
|
238 |
ValidateDataParametersException, CloneNotSupportedException {
|
281 |
239 |
|
282 |
240 |
BufferManager bufferManager = BufferLocator.getBufferManager();
|
283 |
|
int[] structRowCol = calculateStructRowCol(zoomLevel, tileRow, tileCol);
|
284 |
|
int structRow = structRowCol[0];
|
285 |
|
int structCol = structRowCol[1];
|
286 |
241 |
|
287 |
242 |
String keyTile = composeKeyForRecentTiles(zoomLevel, structRow, structCol);
|
288 |
243 |
Tile tile = recentAccededTiles.get(keyTile);
|
... | ... | |
296 |
251 |
RasterStore tileStore = null;
|
297 |
252 |
try {
|
298 |
253 |
tileStore = createTileStore(zoomLevel, structRow, structCol);
|
299 |
|
rasterSet = bufferManager.createBuffer(tileStore.getRasterSet(),false);
|
|
254 |
rasterSet = bufferManager.createBuffer(tileStore.getRasterSet(), false);
|
300 |
255 |
|
301 |
256 |
if (recentAccededTiles.size() > MAX_RECENT_ACCEDED_TILES_NUMBER) {
|
302 |
257 |
removeOlderTile();
|
... | ... | |
305 |
260 |
|
306 |
261 |
return rasterSet.getBand(band);
|
307 |
262 |
} catch (DataException | BufferException e) {
|
308 |
|
logger.warn("Can't fetch tile: zoomLevel = " + zoomLevel + ", tileRow = " + tileRow + ", tileColumn = "
|
309 |
|
+ tileCol + ", band = " + band + ".", e);
|
|
263 |
logger.warn("Can't fetch tile: zoomLevel = " + zoomLevel + ", tileRow = " + structRow + ", tileColumn = "
|
|
264 |
+ structCol + ", band = " + band + ".", e);
|
310 |
265 |
return null;
|
311 |
266 |
} finally {
|
312 |
267 |
if (tileStore != null) {
|
... | ... | |
369 |
324 |
return tileStore;
|
370 |
325 |
}
|
371 |
326 |
|
372 |
|
private File requestTileFile(int zoomLevel, int structRow, int structCol) throws CloneNotSupportedException, CreateEnvelopeException, BufferException, ValidateDataParametersException, DataException {
|
|
327 |
private File requestTileFile(int zoomLevel, int structRow, int structCol) throws CloneNotSupportedException,
|
|
328 |
CreateEnvelopeException, BufferException, ValidateDataParametersException, DataException {
|
373 |
329 |
|
374 |
330 |
RasterQuery rasterQuery = (RasterQuery) this.query.clone();
|
375 |
331 |
|
... | ... | |
378 |
334 |
|
379 |
335 |
GeometryManager geomManager = GeometryLocator.getGeometryManager();
|
380 |
336 |
|
381 |
|
double minX = structExtent.getMinimum(DIMENSIONS.X)+structCol*(pixelSize*columnsPerTile);
|
382 |
|
double minY = structExtent.getMaximum(DIMENSIONS.Y)-((structRow+1)*(pixelSize*rowsPerTile));
|
383 |
|
double maxX = minX + pixelSize*columnsPerTile;
|
384 |
|
double maxY = minY + pixelSize*rowsPerTile;
|
|
337 |
double minX = structExtent.getMinimum(DIMENSIONS.X) + structCol * (pixelSize * columnsPerTile);
|
|
338 |
double minY = structExtent.getMaximum(DIMENSIONS.Y) - ((structRow + 1) * (pixelSize * rowsPerTile));
|
|
339 |
double maxX = minX + pixelSize * columnsPerTile;
|
|
340 |
double maxY = minY + pixelSize * rowsPerTile;
|
385 |
341 |
|
386 |
342 |
Buffer buffer;
|
|
343 |
Buffer clippedBuffer;
|
|
344 |
Buffer interpolatedBuffer;
|
387 |
345 |
|
388 |
|
// try {
|
389 |
|
Envelope envelope = geomManager.createEnvelope(minX, minY, maxX, maxY, SUBTYPES.GEOM2D);
|
390 |
|
// rasterQuery.setClip(envelope);
|
391 |
|
buffer = innerProvider.createBuffer(rasterQuery);
|
392 |
|
buffer = buffer.clip(envelope);
|
393 |
|
buffer = buffer.createInterpolated((int)(buffer.getPixelSizeY()*buffer.getRows()/pixelSize), (int)(buffer.getPixelSizeX()*buffer.getColumns()/pixelSize), Buffer.INTERPOLATION_NearestNeighbour, null);
|
394 |
|
// } catch (LocatorException | BufferException | CreateEnvelopeException e) {
|
395 |
|
// logger.warn("Can't get tile file zoomLevel = "+zoomLevel+", structRow = "+structRow+", structColumn = "+structCol+".", e);
|
396 |
|
// throw new RuntimeException("Can't get tile file zoomLevel = "+zoomLevel+", structRow = "+structRow+", structColumn = "+structCol+".", e);
|
397 |
|
// }
|
|
346 |
Envelope envelope = geomManager.createEnvelope(minX, minY, maxX, maxY, SUBTYPES.GEOM2D);
|
|
347 |
buffer = innerProvider.createBuffer(rasterQuery);
|
|
348 |
clippedBuffer = buffer.clip(envelope);
|
|
349 |
interpolatedBuffer =
|
|
350 |
clippedBuffer.createInterpolated(
|
|
351 |
(int) (clippedBuffer.getPixelSizeY() * clippedBuffer.getRows() / pixelSize),
|
|
352 |
(int) (clippedBuffer.getPixelSizeX() * clippedBuffer.getColumns() / pixelSize),
|
|
353 |
Buffer.INTERPOLATION_NearestNeighbour, null);
|
|
354 |
clippedBuffer = null;
|
398 |
355 |
|
399 |
356 |
String providerName = "GTiff";
|
400 |
357 |
String extension = "tif";
|
... | ... | |
403 |
360 |
DataServerExplorerParameters eparams;
|
404 |
361 |
eparams = manager.createServerExplorerParameters("FilesystemExplorer");
|
405 |
362 |
|
406 |
|
int count = 0;
|
407 |
363 |
StringBuilder builder = new StringBuilder();
|
408 |
364 |
builder.append(tilesFolder);
|
409 |
365 |
builder.append(File.separator);
|
... | ... | |
417 |
373 |
String path = builder.toString();
|
418 |
374 |
File destFile = new File(path);
|
419 |
375 |
File parent = destFile.getParentFile();
|
420 |
|
if(!parent.exists()){
|
|
376 |
if (!parent.exists()) {
|
421 |
377 |
parent.mkdirs();
|
422 |
378 |
}
|
423 |
379 |
eparams.setDynValue("initialpath", path);
|
... | ... | |
431 |
387 |
params.setDynValue("photometric", "RGB");
|
432 |
388 |
params.setDynValue("alpha", "NON-PREMULTIPLIED");
|
433 |
389 |
|
434 |
|
params.setBuffer(buffer);
|
|
390 |
params.setBuffer(interpolatedBuffer);
|
435 |
391 |
serverExplorer.add(providerName, params, true);
|
436 |
392 |
|
437 |
393 |
return destFile;
|
... | ... | |
445 |
401 |
* @throws BufferException
|
446 |
402 |
* @throws CreateEnvelopeException
|
447 |
403 |
*/
|
448 |
|
private RasterStore getDelegatedRasterStore() throws ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
449 |
|
int[] structRowCol = calculateStructRowCol(pixelSizePerZoomLevel.firstKey(), 0, 0);
|
450 |
|
return createTileStore(0, structRowCol[0], structRowCol[1]);
|
|
404 |
private RasterStore getDelegatedRasterStore() throws ValidateDataParametersException, CreateEnvelopeException,
|
|
405 |
BufferException, DataException, CloneNotSupportedException {
|
|
406 |
return createTileStore(0, 0, 0);
|
451 |
407 |
}
|
452 |
408 |
|
453 |
|
|
454 |
409 |
/**
|
455 |
410 |
* @return the band number
|
456 |
411 |
* @throws CloneNotSupportedException
|
... | ... | |
460 |
415 |
* @throws CreateEnvelopeException
|
461 |
416 |
* @throws DynMethodException
|
462 |
417 |
*/
|
463 |
|
public int getBandNumber() throws ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
464 |
|
if(this.bandNumber == null){
|
|
418 |
public int getBandNumber() throws ValidateDataParametersException, CreateEnvelopeException, BufferException,
|
|
419 |
DataException, CloneNotSupportedException {
|
|
420 |
if (this.bandNumber == null) {
|
465 |
421 |
fillStoreInfo();
|
466 |
422 |
}
|
467 |
423 |
return this.bandNumber;
|
... | ... | |
476 |
432 |
* @throws CreateEnvelopeException
|
477 |
433 |
* @throws DynMethodException
|
478 |
434 |
*/
|
479 |
|
public int[] getDataTypes() throws ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
480 |
|
if(this.dataTypes == null){
|
|
435 |
public int[] getDataTypes() throws ValidateDataParametersException, CreateEnvelopeException, BufferException,
|
|
436 |
DataException, CloneNotSupportedException {
|
|
437 |
if (this.dataTypes == null) {
|
481 |
438 |
fillStoreInfo();
|
482 |
439 |
}
|
483 |
440 |
return this.dataTypes;
|
... | ... | |
492 |
449 |
* @throws CreateEnvelopeException
|
493 |
450 |
*
|
494 |
451 |
*/
|
495 |
|
private void fillStoreInfo() throws ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
|
452 |
private void fillStoreInfo() throws ValidateDataParametersException, CreateEnvelopeException, BufferException,
|
|
453 |
DataException, CloneNotSupportedException {
|
496 |
454 |
RasterStore rasterStore = null;
|
497 |
455 |
try {
|
498 |
456 |
rasterStore = getDelegatedRasterStore();
|
499 |
457 |
this.bandNumber = rasterStore.getBands();
|
500 |
|
this.dataTypes=new int[this.bandNumber];
|
501 |
|
for (int i=0;i<this.bandNumber;i++){
|
502 |
|
dataTypes[i]=rasterStore.getBandInfo(i).getDataType();
|
|
458 |
this.dataTypes = new int[this.bandNumber];
|
|
459 |
for (int i = 0; i < this.bandNumber; i++) {
|
|
460 |
dataTypes[i] = rasterStore.getBandInfo(i).getDataType();
|
503 |
461 |
}
|
504 |
462 |
try {
|
505 |
463 |
this.colorInterpretation =
|
... | ... | |
511 |
469 |
}
|
512 |
470 |
|
513 |
471 |
} finally {
|
514 |
|
if(rasterStore != null){
|
|
472 |
if (rasterStore != null) {
|
515 |
473 |
rasterStore.dispose();
|
516 |
474 |
}
|
517 |
475 |
}
|
518 |
476 |
}
|
519 |
477 |
|
520 |
|
|
521 |
478 |
/**
|
522 |
479 |
* @param zoomLevel
|
523 |
480 |
* @return rows per zoom level
|
524 |
481 |
*/
|
525 |
|
public int getRows(int zoomLevel){
|
526 |
|
return (int)(getEnvelope().getLength(DIMENSIONS.Y)/pixelSizePerZoomLevel.get(zoomLevel));
|
|
482 |
public int getRows(int zoomLevel) {
|
|
483 |
return (int) (getEnvelope().getLength(DIMENSIONS.Y) / pixelSizePerZoomLevel.get(zoomLevel));
|
527 |
484 |
}
|
528 |
485 |
|
529 |
486 |
/**
|
530 |
487 |
* @param zoomLevel
|
531 |
488 |
* @return columns per zoom level
|
532 |
489 |
*/
|
533 |
|
public int getColumns(int zoomLevel){
|
534 |
|
return (int)(this.getEnvelope().getLength(DIMENSIONS.X)/pixelSizePerZoomLevel.get(zoomLevel));
|
|
490 |
public int getColumns(int zoomLevel) {
|
|
491 |
return (int) (this.getEnvelope().getLength(DIMENSIONS.X) / pixelSizePerZoomLevel.get(zoomLevel));
|
535 |
492 |
}
|
536 |
493 |
|
537 |
494 |
/**
|
538 |
495 |
* @return envelope per zoom level
|
539 |
496 |
*/
|
540 |
|
public Envelope getEnvelope(){
|
541 |
|
// Envelope envelope = null;
|
542 |
|
|
543 |
|
// if(envelopePerZoomLevel != null){
|
544 |
|
// envelope = envelopePerZoomLevel.get(zoomLevel);
|
545 |
|
// }
|
546 |
|
|
547 |
|
// if(envelope == null){
|
548 |
|
return structExtent;
|
549 |
|
// }
|
550 |
|
//
|
551 |
|
// return envelope;
|
|
497 |
public Envelope getEnvelope() {
|
|
498 |
return structExtent;
|
552 |
499 |
}
|
553 |
500 |
|
554 |
501 |
/**
|
... | ... | |
572 |
519 |
return providerName;
|
573 |
520 |
}
|
574 |
521 |
|
575 |
|
private String composeKeyForRecentTiles(int zoomLevel, int structRow, int structCol){
|
|
522 |
private String composeKeyForRecentTiles(int zoomLevel, int structRow, int structCol) {
|
576 |
523 |
StringBuilder builder = new StringBuilder();
|
577 |
524 |
builder.append(zoomLevel);
|
578 |
525 |
builder.append(":");
|
... | ... | |
582 |
529 |
return builder.toString();
|
583 |
530 |
}
|
584 |
531 |
|
585 |
|
private void removeOlderTile(){
|
|
532 |
private void removeOlderTile() {
|
586 |
533 |
Tile olderTile = null;
|
587 |
534 |
for (Iterator<Tile> iterator = recentAccededTiles.values().iterator(); iterator.hasNext();) {
|
588 |
535 |
Tile tile = (Tile) iterator.next();
|
589 |
|
if(olderTile == null || tile.getLastAccess()<olderTile.getLastAccess()){
|
|
536 |
if (olderTile == null || tile.getLastAccess() < olderTile.getLastAccess()) {
|
590 |
537 |
olderTile = tile;
|
591 |
538 |
}
|
592 |
539 |
}
|
593 |
|
if(olderTile!=null){
|
|
540 |
if (olderTile != null) {
|
594 |
541 |
recentAccededTiles.remove(olderTile.getKey());
|
595 |
542 |
}
|
596 |
543 |
}
|
... | ... | |
604 |
551 |
* @throws BufferException
|
605 |
552 |
* @throws CreateEnvelopeException
|
606 |
553 |
*/
|
607 |
|
public Object getColorInterpretation() throws DynMethodException, ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
608 |
|
if(this.colorInterpretation == null){
|
|
554 |
public Object getColorInterpretation() throws DynMethodException, ValidateDataParametersException,
|
|
555 |
CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
|
556 |
if (this.colorInterpretation == null) {
|
609 |
557 |
fillStoreInfo();
|
610 |
558 |
}
|
611 |
559 |
return this.colorInterpretation;
|
... | ... | |
622 |
570 |
* @throws InitializeException
|
623 |
571 |
* @throws ProviderNotRegisteredException
|
624 |
572 |
*/
|
625 |
|
public Object getColorTable() throws DynMethodException, ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
|
573 |
public Object getColorTable() throws DynMethodException, ValidateDataParametersException, CreateEnvelopeException,
|
|
574 |
BufferException, DataException, CloneNotSupportedException {
|
626 |
575 |
if (this.colorTable == null) {
|
627 |
576 |
fillStoreInfo();
|
628 |
577 |
}
|
... | ... | |
640 |
589 |
* @throws InitializeException
|
641 |
590 |
* @throws ProviderNotRegisteredException
|
642 |
591 |
*/
|
643 |
|
public Object getLegend() throws DynMethodException, ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException, CloneNotSupportedException {
|
644 |
|
if(this.legend == null){
|
|
592 |
public Object getLegend() throws DynMethodException, ValidateDataParametersException, CreateEnvelopeException,
|
|
593 |
BufferException, DataException, CloneNotSupportedException {
|
|
594 |
if (this.legend == null) {
|
645 |
595 |
fillStoreInfo();
|
646 |
596 |
}
|
647 |
597 |
return this.legend;
|
... | ... | |
663 |
613 |
return key;
|
664 |
614 |
}
|
665 |
615 |
|
666 |
|
public org.gvsig.raster.lib.buffer.api.Buffer getBuffer(){
|
|
616 |
public org.gvsig.raster.lib.buffer.api.Buffer getBuffer() {
|
667 |
617 |
lastAccess = System.currentTimeMillis();
|
668 |
618 |
return buffer;
|
669 |
619 |
}
|
... | ... | |
671 |
621 |
public long getLastAccess() {
|
672 |
622 |
return lastAccess;
|
673 |
623 |
}
|
|
624 |
|
|
625 |
@Override
|
|
626 |
protected void finalize() throws Throwable {
|
|
627 |
super.finalize();
|
|
628 |
logger.info("Tile with key " + this.key + " CLEANED.");
|
|
629 |
}
|
674 |
630 |
}
|
675 |
631 |
|
676 |
632 |
}
|
677 |
|
|