Statistics
| Revision:

gvsig-raster / org.gvsig.raster / branches / org.gvsig.raster.2.4 / org.gvsig.raster / org.gvsig.raster.tilecache / org.gvsig.raster.tilecache.provider / src / main / java / org / gvsig / raster / tilecache / provider / FileTileCacheStructImage.java @ 6520

History | View | Annotate | Download (26.5 KB)

1
/* gvSIG. Desktop Geographic Information System.
2
 *
3
 * Copyright ? 2007-2016 gvSIG Association
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
 * MA  02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us
21
 * at info AT gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.raster.tilecache.provider;
24

    
25
import java.io.File;
26
import java.io.IOException;
27
import java.util.ArrayList;
28
import java.util.HashMap;
29
import java.util.Iterator;
30
import java.util.List;
31
import java.util.Map;
32
import java.util.Map.Entry;
33
import java.util.Set;
34
import java.util.TreeMap;
35

    
36
import javax.xml.parsers.DocumentBuilder;
37
import javax.xml.parsers.DocumentBuilderFactory;
38
import javax.xml.parsers.ParserConfigurationException;
39
import javax.xml.transform.OutputKeys;
40
import javax.xml.transform.Transformer;
41
import javax.xml.transform.TransformerException;
42
import javax.xml.transform.TransformerFactory;
43
import javax.xml.transform.dom.DOMSource;
44
import javax.xml.transform.stream.StreamResult;
45

    
46
import org.apache.commons.io.FileUtils;
47
import org.apache.commons.io.FilenameUtils;
48
import org.apache.commons.lang3.StringUtils;
49
import org.cresques.cts.IProjection;
50
import org.slf4j.Logger;
51
import org.slf4j.LoggerFactory;
52
import org.w3c.dom.Attr;
53
import org.w3c.dom.Document;
54
import org.w3c.dom.Element;
55
import org.w3c.dom.NamedNodeMap;
56
import org.w3c.dom.Node;
57
import org.w3c.dom.NodeList;
58

    
59
import org.gvsig.fmap.dal.DALFileLocator;
60
import org.gvsig.fmap.dal.DataServerExplorer;
61
import org.gvsig.fmap.dal.DataServerExplorerParameters;
62
import org.gvsig.fmap.dal.DataStore;
63
import org.gvsig.fmap.dal.DataStoreProviderFactory;
64
import org.gvsig.fmap.dal.exception.DataException;
65
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
66
import org.gvsig.fmap.dal.raster.api.RasterQuery;
67
import org.gvsig.fmap.dal.raster.api.RasterSet;
68
import org.gvsig.fmap.dal.raster.api.RasterStore;
69
import org.gvsig.fmap.dal.raster.spi.NewRasterStoreParameters;
70
import org.gvsig.fmap.dal.raster.spi.RasterStoreProvider;
71
import org.gvsig.fmap.dal.raster.spi.TiledRasterStoreProvider;
72
import org.gvsig.fmap.dal.serverexplorer.filesystem.spi.FilesystemServerExplorerManager;
73
import org.gvsig.fmap.dal.serverexplorer.filesystem.spi.FilesystemServerExplorerProviderFactory;
74
import org.gvsig.fmap.dal.spi.DALSPILocator;
75
import org.gvsig.fmap.dal.spi.DataManagerProviderServices;
76
import org.gvsig.fmap.geom.Geometry.DIMENSIONS;
77
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
78
import org.gvsig.fmap.geom.GeometryLocator;
79
import org.gvsig.fmap.geom.GeometryManager;
80
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
81
import org.gvsig.fmap.geom.primitive.Envelope;
82
import org.gvsig.raster.lib.buffer.api.Band;
83
import org.gvsig.raster.lib.buffer.api.Buffer;
84
import org.gvsig.raster.lib.buffer.api.BufferLocator;
85
import org.gvsig.raster.lib.buffer.api.BufferManager;
86
import org.gvsig.raster.lib.buffer.api.TileStruct;
87
import org.gvsig.raster.lib.buffer.api.exceptions.BufferException;
88
import org.gvsig.raster.lib.buffer.spi.DefaultTileStruct;
89
import org.gvsig.tools.ToolsLocator;
90
import org.gvsig.tools.dispose.Disposable;
91
import org.gvsig.tools.dispose.DisposeUtils;
92
import org.gvsig.tools.dynobject.DynObject;
93
import org.gvsig.tools.exception.BaseException;
94
import org.gvsig.tools.extensionpoint.ExtensionPoint.Extension;
95

    
96
/**
97
 * Represents a tiled image
98
 *
99
 * @author dmartinezizquierdo
100
 *
101
 */
102
public class FileTileCacheStructImage extends AbstractTileCacheStructImage {
103

    
104
    private static final Logger logger = LoggerFactory.getLogger(FileTileCacheStructImage.class);
105
    private static final int MAX_RECENT_ACCEDED_TILES_NUMBER = 50;
106

    
107
    private FilesystemServerExplorerProviderFactory factory;
108
    private String extension;
109
    private File tilesFolder;
110

    
111
    private IProjection crs;
112

    
113
    /**
114
     * @param folder
115
     * @param crs
116
     * @param innerProvider
117
     * @param query
118
     */
119
    public FileTileCacheStructImage(File folder, IProjection crs, RasterStoreProvider innerProvider, RasterQuery query) {
120
        super(innerProvider, query);
121
        this.crs = crs;
122
        GeometryManager geoManager = GeometryLocator.getGeometryManager();
123
        getTileStruct();
124

    
125
        // FIXME
126
        File tilePropertiesFile = new File(folder, "tileCacheStruct.xml");
127
        if (!tilePropertiesFile.exists()) {
128
            createTileCacheStructXMLFile(folder);
129
        }
130

    
131
        TreeMap<Integer, Double> pixelSizePerZoomLevel = new TreeMap<Integer, Double>();
132
        recentAccededTiles = new HashMap<String, Tile>();
133

    
134
        tilesFolder = new File(folder, "V" + File.separatorChar + "Z" + File.separatorChar + "T");
135
        if (!tilesFolder.exists()) {
136
            tilesFolder.mkdirs();
137
        }
138
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
139
        DocumentBuilder dBuilder;
140
        try {
141
            dBuilder = dbFactory.newDocumentBuilder();
142
            Document doc = dBuilder.parse(tilePropertiesFile);
143

    
144
            doc.getDocumentElement().normalize();
145

    
146
            NodeList nList = doc.getElementsByTagName("TileCacheStruct");
147
            for (int temp = 0; temp < nList.getLength(); temp++) {
148

    
149
                Node nNode = nList.item(temp);
150

    
151
                if (nNode.getNodeType() == Node.ELEMENT_NODE) {
152
                    Element eElement = (Element) nNode;
153

    
154
                    NamedNodeMap tileSizeAttributes = eElement.getElementsByTagName("tileSize").item(0).getAttributes();
155
                    tileStruct.setRowsPerTile(Integer.valueOf(tileSizeAttributes.getNamedItem("rows").getNodeValue()));
156
                    tileStruct.setColumnsPerTile(Integer.valueOf(tileSizeAttributes.getNamedItem("columns").getNodeValue()));
157

    
158
                    NodeList elementsByTagName = eElement.getElementsByTagName("format");
159
                    tileStruct.setProviderName(elementsByTagName.item(0).getTextContent());
160

    
161
                    NodeList structExtentList = eElement.getElementsByTagName("structExtent");
162
                    if (structExtentList.getLength() > 0) {
163
                        Element structExtentNode = (Element) structExtentList.item(0);
164
                        if (structExtentNode != null) {
165
                            Double minX =
166
                                Double.valueOf(structExtentNode.getElementsByTagName("minX").item(0).getTextContent());
167
                            Double minY =
168
                                Double.valueOf(structExtentNode.getElementsByTagName("minY").item(0).getTextContent());
169
                            Double maxX =
170
                                Double.valueOf(structExtentNode.getElementsByTagName("maxX").item(0).getTextContent());
171
                            Double maxY =
172
                                Double.valueOf(structExtentNode.getElementsByTagName("maxY").item(0).getTextContent());
173
                            tileStruct.setEnvelope(geoManager.createEnvelope(minX, minY, maxX, maxY, SUBTYPES.GEOM2D));                        }
174
                    }
175

    
176
                    FilesystemServerExplorerManager explorerManager =
177
                        DALFileLocator.getFilesystemServerExplorerManager();
178
                    @SuppressWarnings("unchecked")
179
                    Iterator<Extension> it = explorerManager.getRegisteredProviders();
180
                    while (it.hasNext()) {
181

    
182
                        Extension ext = it.next();
183
                        FilesystemServerExplorerProviderFactory providerFactory;
184
                        try {
185
                            providerFactory = (FilesystemServerExplorerProviderFactory) ext.create();
186
                            if (providerFactory.getName().equalsIgnoreCase(tileStruct.getProviderName())) {
187
                                this.factory = providerFactory;
188
                                break;
189
                            }
190
                        } catch (Exception e) {
191
                            throw new RuntimeException(e);// FIXME !!!
192
                        }
193
                    }
194

    
195
                    NodeList levelsTag = eElement.getElementsByTagName("levels");
196
                    Element levels = (Element) levelsTag.item(0);
197
                    NodeList levelList = levels.getElementsByTagName("level");
198
                    for (int i = 0; i < levelList.getLength(); i++) {
199
                        Element level = (Element) levelList.item(i);
200

    
201
                        NamedNodeMap levelAttributes = level.getAttributes();
202
                        int zoomLevel = Integer.valueOf(levelAttributes.getNamedItem("index").getNodeValue());
203
                        double pixelSize = Double.valueOf(levelAttributes.getNamedItem("pixelSize").getNodeValue());
204

    
205
                        pixelSizePerZoomLevel.put(zoomLevel, pixelSize);
206

    
207
                    }
208
                    this.tileStruct.setPixelSizePerZoomLevel(pixelSizePerZoomLevel);
209
                }
210
            }
211
        } catch (Exception e) {
212
            throw new RuntimeException();
213
        }
214
    }
215

    
216
    /* (non-Javadoc)
217
     * @see org.gvsig.raster.tilecache.provider.ITileStructImage#fetchTile(int, int, int, int)
218
     */
219
    @Override
220
    public Band fetchTile(int bandNumber, int zoomLevel, int structRow, int structCol) throws CreateEnvelopeException,
221
        ValidateDataParametersException, CloneNotSupportedException {
222

    
223
        BufferManager bufferManager = BufferLocator.getBufferManager();
224

    
225
        String keyTile = composeKeyForRecentTiles(zoomLevel, structRow, structCol);
226
        Tile tile = recentAccededTiles.get(keyTile);
227

    
228
        if (tile != null) {
229
            // Devolver la banda del buffer del tile
230
            return tile.getBuffer().getBand(bandNumber);
231
        } else {
232
            // Cargar un tile nuevo
233
            Buffer rasterSet = null;
234
            RasterStore tileStore = null;
235
            RasterSet tileStoreRasterSet = null;
236
            try {
237
                tileStore = createTileStore(zoomLevel, structRow, structCol);
238
                tileStoreRasterSet = tileStore.getRasterSet();
239
                rasterSet = bufferManager.createBuffer(tileStoreRasterSet, false);
240

    
241
                if (recentAccededTiles.size() >= MAX_RECENT_ACCEDED_TILES_NUMBER) {
242
                    removeOlderTile();
243
                }
244
                recentAccededTiles.put(keyTile, new Tile(rasterSet, keyTile));
245

    
246
                Band band = rasterSet.getBand(bandNumber);
247
                ToolsLocator.getDisposableManager().bind(band);
248
                return band;
249
            } catch (DataException | BufferException e) {
250
                logger.warn("Can't fetch tile: zoomLevel = " + zoomLevel + ", tileRow = " + structRow
251
                    + ", tileColumn = " + structCol + ", band = " + bandNumber + ".", e);
252
                return null;
253
            } finally {
254
                DisposeUtils.dispose(tileStore);
255
                tileStore = null;
256
                DisposeUtils.dispose(tileStoreRasterSet);
257
                tileStoreRasterSet = null;
258
                DisposeUtils.dispose(rasterSet);
259
                rasterSet = null;
260
            }
261
        }
262
    }
263

    
264
    private RasterStore createTileStore(int zoomLevel, int structRow, int structCol)
265
        throws ValidateDataParametersException, CreateEnvelopeException, BufferException, DataException,
266
        CloneNotSupportedException {
267

    
268
        File tileFile = null;
269
        File zoomFolder = new File(tilesFolder, String.valueOf(zoomLevel));
270
        if (!zoomFolder.exists()) {
271
            zoomFolder.mkdirs();
272
        }
273
        File firstColumnFolder = new File(zoomFolder, String.valueOf(structCol));
274
        if (!firstColumnFolder.exists()) {
275
            tileFile = requestTileFile(zoomLevel, structRow, structCol);
276
            this.extension = FilenameUtils.getExtension(tileFile.getAbsolutePath());
277
        }
278
        String rowBaseName = String.valueOf(structRow);
279
        if (this.extension == null) {
280
            File[] providerAcceptedFiles = firstColumnFolder.listFiles(factory);
281
            for (int i = 0; i < providerAcceptedFiles.length; i++) {
282
                File providerAcceptedFile = providerAcceptedFiles[i];
283
                if (FilenameUtils.getBaseName(providerAcceptedFile.getAbsolutePath()).equalsIgnoreCase(rowBaseName)) {
284
                    tileFile = providerAcceptedFile;
285
                }
286
            }
287
            if (tileFile == null || !tileFile.exists()) {
288
                tileFile = requestTileFile(zoomLevel, structRow, structCol);
289
            }
290
            this.extension = FilenameUtils.getExtension(tileFile.getAbsolutePath());
291
        } else {
292
            if (tileFile == null) {
293
                tileFile =
294
                    new File(firstColumnFolder.getAbsolutePath() + File.separatorChar + rowBaseName + "."
295
                        + this.extension);
296
            }
297
        }
298
        if (!tileFile.exists()) {
299
            tileFile = requestTileFile(zoomLevel, structRow, structCol);
300
        }
301
        RasterStore tileStore = null;
302
        DataManagerProviderServices manager = DALSPILocator.getDataManagerProviderServices();
303
        DataStoreProviderFactory providerFactory = manager.getStoreProviderFactory(this.tileStruct.getProviderName());
304

    
305
        DynObject params = providerFactory.createParameters();
306
        if (params.getDynClass().getDynField("file") != null) {
307
            params.setDynValue("file", tileFile);
308
        }
309
        if (params.getDynClass().getDynField("crs") != null) {
310
            params.setDynValue("crs", this.crs);
311
        }
312

    
313
        tileStore = (RasterStore) manager.openStore(this.tileStruct.getProviderName(), params);
314
        return tileStore;
315
    }
316

    
317
    private File requestTileFile(int zoomLevel, int structRow, int structCol) throws CreateEnvelopeException,
318
        CloneNotSupportedException, BufferException, ValidateDataParametersException, DataException {
319

    
320
        RasterQuery rasterQuery = (RasterQuery) this.query.clone();
321

    
322
        Double pixelSize = this.tileStruct.getPixelSizePerZoomLevel().get(zoomLevel);
323
        rasterQuery.setPixelSize(pixelSize);
324

    
325
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
326

    
327
        Envelope structExtent = this.tileStruct.getEnvelope();
328
        int rowsPerTile = this.tileStruct.getRowsPerTile();
329
        int columnsPerTile = this.tileStruct.getColumnsPerTile();
330
        double minX = structExtent.getMinimum(DIMENSIONS.X) + structCol * (pixelSize * columnsPerTile);
331
        double minY = structExtent.getMaximum(DIMENSIONS.Y) - ((structRow + 1) * (pixelSize * rowsPerTile));
332
        double maxX = minX + pixelSize * columnsPerTile;
333
        double maxY = minY + pixelSize * rowsPerTile;
334
        Envelope envelope = geomManager.createEnvelope(minX, minY, maxX, maxY, SUBTYPES.GEOM2D);
335

    
336
        Buffer buffer = null;
337
        Buffer clippedBuffer = null;
338
        Buffer interpolatedBuffer = null;
339
        File destFile = null;
340

    
341
        try {
342
            buffer = innerProvider.createBuffer(rasterQuery);
343
            clippedBuffer = buffer.clip(envelope);
344
            interpolatedBuffer =
345
                clippedBuffer.createInterpolated(
346
                    (int) (Math.round(clippedBuffer.getPixelSizeY() * clippedBuffer.getRows() / pixelSize)),
347
                    (int) (Math.round(clippedBuffer.getPixelSizeX() * clippedBuffer.getColumns() / pixelSize)),
348
                    Buffer.INTERPOLATION_NearestNeighbour, null);
349

    
350
            String providerName = "GTiff";
351
            String extension = "tif";
352

    
353
            DataManagerProviderServices manager = DALSPILocator.getDataManagerProviderServices();
354
            DataServerExplorerParameters eparams;
355
            eparams = manager.createServerExplorerParameters("FilesystemExplorer");
356

    
357
            StringBuilder builder = new StringBuilder();
358
            builder.append(tilesFolder);
359
            builder.append(File.separator);
360
            builder.append(zoomLevel);
361
            builder.append(File.separator);
362
            builder.append(structCol);
363
            builder.append(File.separator);
364
            builder.append(structRow);
365
            builder.append(".");
366
            builder.append(extension);
367
            String path = builder.toString();
368
            destFile = new File(path);
369
            File parent = destFile.getParentFile();
370
            if (!parent.exists()) {
371
                parent.mkdirs();
372
            }
373
            eparams.setDynValue("initialpath", path);
374
            DataServerExplorer serverExplorer = manager.createServerExplorer(eparams);
375

    
376
            NewRasterStoreParameters params = (NewRasterStoreParameters) serverExplorer.getAddParameters(providerName);
377
            params.setDynValue("file", destFile);
378

    
379
            params.setDynValue("compress", "NONE");
380
            params.setDynValue("tfw", false);
381

    
382
            // FIXME: Ver de coger estos dos valores del inner provider para que
383
            // los tiles sean homog?neos con el raster original
384
            int bands = innerProvider.getBands();
385
            switch (bands) {
386
            case 3:
387
                params.setDynValue("photometric", "RGB");
388
                break;
389
            case 4:
390
                params.setDynValue("photometric", "RGB");
391
                params.setDynValue("alpha", "NON-PREMULTIPLIED");
392
                break;
393
            default:
394
                params.setDynValue("photometric", "MIN_IS_BLACK");
395
                break;
396
            }
397

    
398
            params.setBuffer(interpolatedBuffer);
399
            serverExplorer.add(providerName, params, true);
400
        } finally {
401
            DisposeUtils.dispose(buffer);
402
            DisposeUtils.dispose(clippedBuffer);
403
            DisposeUtils.dispose(interpolatedBuffer);
404
        }
405

    
406
        return destFile;
407
    }
408

    
409
    @Override
410
    protected void doDispose() throws BaseException {
411
        if (recentAccededTiles != null) {
412
            Set<Entry<String, Tile>> entrySet = recentAccededTiles.entrySet();
413
            for (Iterator<Entry<String, Tile>> iterator = entrySet.iterator(); iterator.hasNext();) {
414
                Entry<String, Tile> entry = (Entry<String, Tile>) iterator.next();
415
                DisposeUtils.dispose(entry.getValue());
416
            }
417
            recentAccededTiles.clear();
418
        }
419
        DisposeUtils.dispose(innerProvider);
420
        innerProvider = null;
421
        DisposeUtils.dispose((Disposable) tileStruct);
422
        tileStruct = null;
423

    
424
        query = null;
425
        factory = null;
426
        crs = null;
427
        colorInterpretation = null;
428
        legend = null;
429
        colorTable = null;
430
    }
431

    
432
    private void createTileCacheStructXMLFile(File folder) {
433

    
434
        TileStruct tileStructProvider = null;
435

    
436
        if (innerProvider != null && innerProvider instanceof TiledRasterStoreProvider) {
437
            tileStructProvider = ((TiledRasterStoreProvider) innerProvider).getTileStruct();
438
        }
439

    
440
        int tileSizeX = TileCacheLibrary.DEFAULT_TILEWIDTH;
441
        if(tileStructProvider!=null && tileStructProvider.getColumnsPerTile()!=0){
442
            tileSizeX = tileStructProvider.getColumnsPerTile();
443
        }
444

    
445
        int tileSizeY = TileCacheLibrary.DEFAULT_TILEHEIGHT;
446
        if(tileStructProvider!=null && tileStructProvider.getRowsPerTile()!=0){
447
            tileSizeY = tileStructProvider.getRowsPerTile();
448
        }
449

    
450
        File file = new File(folder, "tileCacheStruct.xml");
451

    
452
        if (!file.exists()) {
453
            Buffer auxBuffer = null;
454
            try {
455
                DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
456
                DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
457

    
458
                // root elements
459
                Document doc = (Document) docBuilder.newDocument();
460
                Element rootElement = doc.createElement("TileCacheStruct");
461
                doc.appendChild(rootElement);
462

    
463
                // tileSize
464
                Element tileSize = doc.createElement("tileSize");
465
                rootElement.appendChild(tileSize);
466

    
467
                // tileSize Rows
468
                Attr rows = doc.createAttribute("rows");
469
                rows.setValue(String.valueOf(tileSizeY));
470
                tileSize.setAttributeNode(rows);
471

    
472
                // tileSize Columns
473
                Attr columns = doc.createAttribute("columns");
474
                columns.setValue(String.valueOf(tileSizeX));
475
                tileSize.setAttributeNode(columns);
476

    
477
                // format
478
                Element format = doc.createElement("format");
479
                if(tileStructProvider==null || StringUtils.isEmpty(tileStructProvider.getProviderName())){
480
                    format.setTextContent("GTiff");
481
                }
482
                rootElement.appendChild(format);
483

    
484
                // structExtent
485
                Element structExtent = doc.createElement("structExtent");
486
                rootElement.appendChild(structExtent);
487

    
488
                Envelope envelope = (Envelope) innerProvider.getDynValue(DataStore.METADATA_ENVELOPE);
489

    
490
                // structExtent minX
491
                Element minX = doc.createElement("minX");
492
                minX.setTextContent(String.valueOf(envelope.getMinimum(DIMENSIONS.X)));
493
                structExtent.appendChild(minX);
494

    
495
                // structExtent minY
496
                Element minY = doc.createElement("minY");
497
                minY.setTextContent(String.valueOf(envelope.getMinimum(DIMENSIONS.Y)));
498
                structExtent.appendChild(minY);
499

    
500
                // structExtent maxX
501
                Element maxX = doc.createElement("maxX");
502
                maxX.setTextContent(String.valueOf(envelope.getMaximum(DIMENSIONS.X)));
503
                structExtent.appendChild(maxX);
504

    
505
                // structExtent maxY
506
                Element maxY = doc.createElement("maxY");
507
                maxY.setTextContent(String.valueOf(envelope.getMaximum(DIMENSIONS.Y)));
508
                structExtent.appendChild(maxY);
509

    
510
                Element levels = doc.createElement("levels");
511
                rootElement.appendChild(levels);
512

    
513
                List<Double> pixelSizes = new ArrayList<Double>();
514
                Map<Integer, Double> zoomLevels = new HashMap<Integer, Double>();
515

    
516
                if (tileStructProvider==null || tileStructProvider.getPixelSizePerZoomLevel() == null) {
517
                    // FIXME: ?alguna otra forma de obtener el pixelSize del store completo?
518
                    auxBuffer = innerProvider.createBuffer(null);
519
                    int completeRows = auxBuffer.getRows();
520
                    int completeColumns = auxBuffer.getColumns();
521
                    double completePixelSizeX = auxBuffer.getPixelSizeX();
522
                    double completePixelSizeY = auxBuffer.getPixelSizeY();
523

    
524
                    int maximumLength = completeColumns;
525
                    int tileMaximumLength = tileSizeX;
526
                    double maximumPixelSize = completePixelSizeX;
527

    
528
                    if (completeRows * tileSizeY > completeColumns * tileSizeX) {
529
                        maximumLength = completeRows;
530
                        tileMaximumLength = tileSizeY;
531
                        maximumPixelSize = completePixelSizeY;
532
                    }
533

    
534
                    double pixelSize = maximumPixelSize;
535
                    while (maximumLength * maximumPixelSize / pixelSize > tileMaximumLength) {
536
                        pixelSizes.add(pixelSize);
537
                        pixelSize = pixelSize * 2;
538
                    }
539
                    pixelSizes.add(pixelSize);
540
                    int i = 0;
541
                    for (int zoomLevel = pixelSizes.size() - 1; zoomLevel >= 0; zoomLevel--) {
542
                        zoomLevels.put(i, pixelSizes.get(zoomLevel));
543
                        appendZoomLevel(doc, levels, i, pixelSizes.get(zoomLevel));
544
                        i++;
545
                    }
546
                } else {
547
                    zoomLevels = tileStructProvider.getPixelSizePerZoomLevel();
548
                }
549

    
550
                for (Entry<Integer, Double> entry : zoomLevels.entrySet()) {
551
                    appendZoomLevel(doc, levels, entry.getKey(), entry.getValue());
552
                }
553

    
554
                // write the content into xml file
555

    
556
                DOMSource source = new DOMSource(doc);
557
                File parent = file.getParentFile();
558
                if (!parent.exists()) {
559
                    parent.mkdirs();
560
                }
561
                file.createNewFile();
562
                StreamResult result = new StreamResult(file.toURI().getPath());
563

    
564
                TransformerFactory transformerFactory = TransformerFactory.newInstance();
565
                Transformer transformer = transformerFactory.newTransformer();
566
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
567
                transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
568
                transformer.transform(source, result);
569

    
570
            } catch (ParserConfigurationException | TransformerException | IOException | BufferException e) {
571
                logger.warn("Can't create tileCacheStruct.xml file", e);
572
            } finally {
573
                DisposeUtils.dispose(auxBuffer);
574
                auxBuffer = null;
575
            }
576
        }
577
    }
578

    
579
    /**
580
     * @param doc
581
     * @param rootElement
582
     * @param levels
583
     * @param zoomLevel
584
     * @param pixelSize
585
     */
586
    private void appendZoomLevel(Document doc, Element levels, int zoomLevel, double pixelSize) {
587
        Element level = doc.createElement("level");
588
        levels.appendChild(level);
589

    
590
        // level zoomLevel
591
        Attr index = doc.createAttribute("index");
592
        index.setValue(String.valueOf(zoomLevel));
593
        level.setAttributeNode(index);
594

    
595
        // level pixelSize
596
        Attr pxSize = doc.createAttribute("pixelSize");
597
        pxSize.setValue(String.valueOf(pixelSize));
598
        level.setAttributeNode(pxSize);
599
    }
600

    
601
    /* (non-Javadoc)
602
     * @see org.gvsig.raster.tilecache.provider.ITileStructImage#getTileStruct()
603
     */
604
    @Override
605
    public TileStruct getTileStruct() {
606
        if(this.tileStruct==null){
607
            this.tileStruct = new DefaultTileStruct();
608
        }
609
        return this.tileStruct;
610
    }
611

    
612
    public void forgetCache() {
613
        super.forgetCache();
614
        if(tilesFolder.exists() && tilesFolder.isDirectory()){
615
            File[] files = tilesFolder.listFiles();
616
            for(int i=0; i<files.length; i++){
617
                try {
618
                    FileUtils.forceDelete(files[i]);
619
                } catch (IOException e) {
620
                    logger.warn("Can't delete the file "+files[i].getAbsolutePath(), e);
621
                }
622
            }
623
        }
624
    }
625
}