Statistics
| Revision:

gvsig-raster / org.gvsig.raster / branches / org.gvsig.raster.2.4 / org.gvsig.wmts / org.gvsig.wmts.provider / src / main / java / org / gvsig / wmts / provider / WMTSImage.java @ 8842

History | View | Annotate | Download (16.3 KB)

1
package org.gvsig.wmts.provider;
2

    
3
import java.awt.image.BufferedImage;
4
import java.io.File;
5
import java.io.IOException;
6
import java.net.ConnectException;
7
import java.net.URL;
8
import java.util.HashMap;
9
import java.util.Iterator;
10
import java.util.Map;
11

    
12
import javax.imageio.ImageIO;
13

    
14
import org.cresques.cts.IProjection;
15
import org.slf4j.Logger;
16
import org.slf4j.LoggerFactory;
17

    
18
import org.gvsig.compat.CompatLocator;
19
import org.gvsig.compat.net.Downloader;
20
import org.gvsig.fmap.dal.exception.DataException;
21
import org.gvsig.fmap.dal.exception.ValidateDataParametersException;
22
import org.gvsig.fmap.geom.Geometry.DIMENSIONS;
23
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
24
import org.gvsig.fmap.geom.GeometryLocator;
25
import org.gvsig.fmap.geom.GeometryManager;
26
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
27
import org.gvsig.fmap.geom.exception.CreateGeometryException;
28
import org.gvsig.fmap.geom.primitive.Envelope;
29
import org.gvsig.raster.lib.buffer.api.Band;
30
import org.gvsig.raster.lib.buffer.api.Buffer;
31
import org.gvsig.raster.lib.buffer.api.BufferLocator;
32
import org.gvsig.raster.lib.buffer.api.BufferManager;
33
import org.gvsig.raster.lib.buffer.api.TileStruct;
34
import org.gvsig.raster.lib.buffer.api.exceptions.BandException;
35
import org.gvsig.raster.lib.buffer.api.exceptions.BufferException;
36
import org.gvsig.raster.wmts.ogc.WMTSStatus;
37
import org.gvsig.raster.wmts.ogc.struct.WMTSTileMatrix;
38
import org.gvsig.tools.ToolsLocator;
39
import org.gvsig.tools.dispose.Disposable;
40
import org.gvsig.tools.dispose.DisposeUtils;
41
import org.gvsig.tools.dispose.impl.AbstractDisposable;
42
import org.gvsig.tools.exception.BaseException;
43
import org.gvsig.tools.locator.LocatorException;
44

    
45

    
46
/**
47
 * @author fdiaz
48
 *
49
 */
50
public class WMTSImage extends AbstractDisposable {
51

    
52
    private static final Logger logger = LoggerFactory.getLogger(WMTSImage.class);
53

    
54
    private static final int MAX_RECENT_ACCEDED_TILES_NUMBER = 25;
55

    
56
    private TileStruct tileStruct;
57
    private WMTSRasterProvider provider;
58
    private DownloadedTile tile;
59
    private Map<Integer, WMTSTileMatrix> tileMatrixPerZoomLevel = null;
60
    protected Map<String, DownloadedTile> recentAccededWMTSTiles;
61

    
62
    private IProjection projection;
63

    
64

    
65
    /**
66
     * @param provider
67
     * @param query
68
     * @param tileStruct
69
     * @param tileMatrixPerZoomLevel
70
     */
71
    public WMTSImage(TileStruct tileStruct, Map<Integer, WMTSTileMatrix> tileMatrixPerZoomLevel, WMTSRasterProvider provider) {
72
        this.tileStruct = tileStruct;
73
        DisposeUtils.bind((Disposable) this.tileStruct);
74
        this.provider = provider;
75
        this.projection = provider.getProjection();
76
        this.tile = null;
77
        this.tileMatrixPerZoomLevel = tileMatrixPerZoomLevel;
78
        this.recentAccededWMTSTiles = new HashMap<String, DownloadedTile>();
79

    
80
    }
81

    
82
    /**
83
     * @param bandNumber
84
     * @param zoomLevel
85
     * @param structRow
86
     * @param structCol
87
     * @return Band
88
     * @throws CreateEnvelopeException
89
     * @throws CloneNotSupportedException
90
     * @throws ValidateDataParametersException
91
     * @throws IOException
92
     * @throws ConnectException
93
     */
94
    public Band fetchTile(int bandNumber, int zoomLevel, int structRow, int structCol) throws CreateEnvelopeException,
95
        ValidateDataParametersException, CloneNotSupportedException, ConnectException, IOException {
96

    
97
        String keyTile = composeKeyForRecentTiles(zoomLevel, structRow, structCol);
98

    
99
        Buffer buffer = null;
100
        if (this.tile != null && this.tile.getKey().equalsIgnoreCase(keyTile)) {
101

    
102
            // Devolver la banda del buffer del tile
103
//            logger.info("Devolviendo la banda " + bandNumber + " del buffer del tile " + tile.getKey());
104
            buffer = tile.getBuffer();
105
            Band band = buffer.getBand(bandNumber);
106
            return band;
107
        } else {
108
            if(this.tile != null) {
109
                DisposeUtils.dispose(this.tile);
110
            }
111
            this.tile = null;
112
            // Cargar un tile nuevo
113
            Band band = null;
114
            try {
115
                while (this.tile == null) {
116
                    tile = requestTile(zoomLevel, structRow, structCol, keyTile);
117
                }
118

    
119
                buffer = tile.getBuffer();
120

    
121
                band = buffer.getBand(bandNumber);
122
                return band;
123
            } catch (DataException | BufferException | CreateGeometryException | BandException e) {
124
                logger.warn("Can't fetch tile: zoomLevel = " + zoomLevel + ", tileRow = " + structRow
125
                    + ", tileColumn = " + structCol + ", band = " + bandNumber + ".", e);
126
                return null;
127
            }
128
        }
129
    }
130

    
131
    private DownloadedTile requestTile(int zoomLevel, int structRow, int structCol, String key) throws CreateEnvelopeException,
132
 CloneNotSupportedException, BufferException, ValidateDataParametersException,
133
        DataException, CreateGeometryException, ConnectException, IOException, BandException {
134

    
135
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
136
        BufferManager bufferManager = BufferLocator.getBufferManager();
137

    
138
        // C?lculo del envelope del tile que queremos construir
139
        Envelope envelope = createRequestTileEnvelope(zoomLevel, structRow, structCol, geomManager);
140

    
141
        int[] bandDataTypes =
142
            { BufferManager.TYPE_BYTE, BufferManager.TYPE_BYTE, BufferManager.TYPE_BYTE, BufferManager.TYPE_BYTE };
143

    
144
        WMTSTileMatrix tileMatrix = tileMatrixPerZoomLevel.get(zoomLevel);
145

    
146
        WMTSStatus status = provider.getWMTSStatus();
147
        String tileMatrixIdentifier = tileMatrix.getIdentifier();
148
        status.setTileMatrix(tileMatrixIdentifier);
149

    
150
        Envelope affectedEnvelope = null;
151
        int firstAffectedRow = getTileMatrixRow(zoomLevel, tileMatrix, envelope.getMaximum(DIMENSIONS.Y));
152
        int firstAffectedColumn = getTileMatrixColumn(zoomLevel, tileMatrix, envelope.getMinimum(DIMENSIONS.X));
153
        int lastAffectedRow = getTileMatrixRow(zoomLevel, tileMatrix, envelope.getMinimum(DIMENSIONS.Y));
154
        int lastAffectedColumn = getTileMatrixColumn(zoomLevel, tileMatrix, envelope.getMaximum(DIMENSIONS.X));
155

    
156
        for (int row=firstAffectedRow; row<=lastAffectedRow; row++) {
157
            for (int column = firstAffectedColumn; column <= lastAffectedColumn; column++) {
158
                Envelope tileEnvelope = getTileEnvelope(zoomLevel, tileMatrix, row, column);
159
                if (affectedEnvelope == null) {
160
                    affectedEnvelope = tileEnvelope;
161
                } else {
162
                    affectedEnvelope.add(tileEnvelope);
163
                }
164
            }
165
        }
166

    
167

    
168
        Buffer affectedBuffer = null;
169
        try {
170

    
171
            affectedBuffer =
172
                bufferManager.createBuffer((lastAffectedRow - firstAffectedRow + 1) * tileMatrix.getTileHeight(),
173
                    (lastAffectedColumn - firstAffectedColumn + 1) * tileMatrix.getTileWidth(), bandDataTypes, null,
174
                    this.projection, affectedEnvelope);
175
            int targetTileRow = 0;
176
            int sourceRow = 0;
177
            for (int row = firstAffectedRow; row <= lastAffectedRow; row++) {
178
                int targetTileColumn = 0;
179
                int sourceColumn = 0;
180
                sourceRow = targetTileRow * tileMatrix.getTileHeight();
181
                for (int column = firstAffectedColumn; column <= lastAffectedColumn; column++) {
182
                    sourceColumn = targetTileColumn * tileMatrix.getTileWidth();
183

    
184
                    String keyTile = composeKeyForRecentTiles(tileMatrixIdentifier, zoomLevel, row, column);
185
                    DownloadedTile tile = recentAccededWMTSTiles.get(keyTile);
186
                    Buffer sourceTileBuffer = null;
187
                    if (tile == null) {
188
                        status.setTileCol(column);
189
                        status.setTileRow(row);
190
                        Envelope tileEnvelope = getTileEnvelope(zoomLevel, tileMatrix, row, column);
191

    
192
                        URL url = provider.getWMTSClient().getTileURL(status);
193

    
194
                        Downloader downloader = CompatLocator.getDownloader();
195
                        BufferedImage img;
196
                        try {
197
                            logger.info("URL: " + url.toString());
198
                            File file = downloader.downloadFile(url, "WMTS_tile", null);
199
                            img = ImageIO.read(file);
200
                        } catch (Exception e) {
201
                            continue;
202
                            // throw new BufferException(e);
203
                        }
204
                        sourceTileBuffer = bufferManager.createRGBABuffer(img, projection, tileEnvelope);
205
                        if (recentAccededWMTSTiles.size() >= MAX_RECENT_ACCEDED_TILES_NUMBER) {
206
                            removeOlderTile();
207
                        }
208
                        DownloadedTile downloadedTile = new DownloadedTile(sourceTileBuffer, keyTile);
209
                        recentAccededWMTSTiles.put(keyTile, downloadedTile);
210
                        DisposeUtils.dispose(sourceTileBuffer);
211

    
212
                    } else {
213
                        sourceTileBuffer = tile.getBuffer();
214
                    }
215

    
216
                    for (int iband = 0; iband < affectedBuffer.getBandCount(); iband++) {
217

    
218
                        Band affectedBand = affectedBuffer.getBand(iband);
219
                        affectedBand.copyFrom(sourceTileBuffer.getBand(iband), sourceRow, sourceColumn);
220

    
221
                    }
222

    
223
                    targetTileColumn++;
224
                }
225
                targetTileRow++;
226
            }
227
            Buffer clipped = null;
228
            try {
229
                clipped = affectedBuffer.clip(envelope);
230

    
231
                DownloadedTile downloadedTile = new DownloadedTile(clipped, key);
232
                return downloadedTile;
233
            } finally {
234
                DisposeUtils.disposeQuietly(clipped);
235
            }
236
        } finally {
237
            DisposeUtils.disposeQuietly(affectedBuffer);
238
        }
239

    
240
    }
241

    
242
    private int getTileMatrixRow(int zoomLevel, WMTSTileMatrix tileMatrix, double y) {
243
        Double pixelSize = tileStruct.getPixelSizePerZoomLevel().get(zoomLevel);
244
        double heightWCTile = tileMatrix.getTileHeight() * pixelSize;
245

    
246
        double[] topLeftCorner = tileMatrix.getTopLeftCorner();
247
        double top;
248
            top = topLeftCorner[1];
249
        double row = (top-y)/heightWCTile;
250
        return (int)row;
251
    }
252

    
253
    private int getTileMatrixColumn(int zoomLevel, WMTSTileMatrix tileMatrix, double x) {
254
        Double pixelSize = tileStruct.getPixelSizePerZoomLevel().get(zoomLevel);
255
        double widthWCTile = tileMatrix.getTileWidth() * pixelSize;
256

    
257
        double[] topLeftCorner = tileMatrix.getTopLeftCorner();
258
        double left;
259
        left = topLeftCorner[0];
260
        double row = (x - left) / widthWCTile;
261
        return (int) row;
262
    }
263

    
264
    private String composeKeyForRecentTiles(String tileMatrixIdentifier, int zoomLevel, int row, int col) {
265
        StringBuilder builder = new StringBuilder();
266
        builder.append(tileMatrixIdentifier);
267
        builder.append(":");
268
        builder.append(zoomLevel);
269
        builder.append(":");
270
        builder.append(col);
271
        builder.append(":");
272
        builder.append(row);
273
        return builder.toString();
274
    }
275

    
276

    
277
    /**
278
     * C?lculo del envelope del tile que queremos construir
279
     *
280
     * @param zoomLevel
281
     * @param structRow
282
     * @param structCol
283
     * @param geomManager
284
     * @return
285
     * @throws CreateEnvelopeException
286
     */
287
    private Envelope createRequestTileEnvelope(int zoomLevel, int structRow, int structCol, GeometryManager geomManager)
288
        throws CreateEnvelopeException {
289
        Double pixelSize = this.tileStruct.getPixelSizePerZoomLevel().get(zoomLevel);
290

    
291
        Envelope structExtent = this.tileStruct.getEnvelope();
292
        int rowsPerTile = this.tileStruct.getRowsPerTile();
293
        int columnsPerTile = this.tileStruct.getColumnsPerTile();
294

    
295
        double minX =
296
            Math.max(structExtent.getMinimum(DIMENSIONS.X) + (structCol * (pixelSize * columnsPerTile)),
297
                structExtent.getMinimum(DIMENSIONS.X));
298
        double minY =
299
            Math.max(structExtent.getMaximum(DIMENSIONS.Y) - ((structRow + 1) * (pixelSize * rowsPerTile)),
300
                structExtent.getMinimum(DIMENSIONS.Y));
301
        double maxX =
302
            Math.min(structExtent.getMinimum(DIMENSIONS.X) + ((structCol + 1) * (pixelSize * columnsPerTile)),
303
                structExtent.getMaximum(DIMENSIONS.X));
304
        double maxY =
305
            Math.min(structExtent.getMaximum(DIMENSIONS.Y) - (structRow * (pixelSize * rowsPerTile)),
306
                structExtent.getMaximum(DIMENSIONS.Y));
307
        Envelope envelope = geomManager.createEnvelope(minX, minY, maxX, maxY, SUBTYPES.GEOM2D);
308
        return envelope;
309
    }
310

    
311
    private Envelope getTileEnvelope(int zoomLevel, WMTSTileMatrix tileMatrix, int row, int column)
312
        throws LocatorException, CreateEnvelopeException {
313
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
314

    
315
        // PRUEBA V?LIDA: tileMatrix.getWidthWCTile y tileMatrix.getHeightWCTile
316
        // no devuelven lo que esperamos aqu?
317
        Double pixelSize = tileStruct.getPixelSizePerZoomLevel().get(zoomLevel);
318
        double widthWCTile = tileMatrix.getTileWidth() * pixelSize;
319
        double heightWCTile = tileMatrix.getTileHeight() * pixelSize;
320

    
321
        double[] topLeftCorner = tileMatrix.getTopLeftCorner();
322
        double tileMinX;
323
        double tileMaxY;
324
        tileMinX = topLeftCorner[0] + (column * widthWCTile);
325
        tileMaxY = topLeftCorner[1] - (row * heightWCTile);
326
        double tileMaxX = tileMinX + widthWCTile;
327
        double tileMinY = tileMaxY - heightWCTile;
328

    
329
        Envelope envelope = geomManager.createEnvelope(tileMinX, tileMinY, tileMaxX, tileMaxY, SUBTYPES.GEOM2D);
330

    
331
        return envelope;
332
    }
333

    
334
    private String composeKeyForRecentTiles(int zoomLevel, int structRow, int structCol) {
335
        StringBuilder builder = new StringBuilder();
336
        builder.append(zoomLevel);
337
        builder.append(":");
338
        builder.append(structCol);
339
        builder.append(":");
340
        builder.append(structRow);
341
        return builder.toString();
342
    }
343

    
344
    private class DownloadedTile extends AbstractDisposable {
345

    
346
        org.gvsig.raster.lib.buffer.api.Buffer buffer;
347
        long lastAccess;
348
        String key;
349

    
350
        public DownloadedTile(org.gvsig.raster.lib.buffer.api.Buffer buffer, String key) {
351
//            logger
352
//                .info("CONSTRUCTOR hashCode = " + this.hashCode() + " className = " + this.getClass().getSimpleName());
353
            ToolsLocator.getDisposableManager().bind(buffer);
354
            this.buffer = buffer;
355
            this.key = key;
356
            lastAccess = System.currentTimeMillis();
357
        }
358

    
359
        public String getKey() {
360
            return key;
361
        }
362

    
363
        public org.gvsig.raster.lib.buffer.api.Buffer getBuffer() {
364
            lastAccess = System.currentTimeMillis();
365
            return buffer;
366
        }
367

    
368
        public long getLastAccess() {
369
            return lastAccess;
370
        }
371

    
372
        @Override
373
        protected void finalize() throws Throwable {
374
            super.finalize();
375
//            logger.info("CLEANED key " + this.key + " hashCode = " + this.hashCode());
376
        }
377

    
378
        @Override
379
        protected void doDispose() throws BaseException {
380
            DisposeUtils.dispose(buffer);
381
            buffer = null;
382
        }
383
    }
384

    
385
    @Override
386
    protected void doDispose() throws BaseException {
387
        if(this.tile != null) {
388
            DisposeUtils.dispose(this.tile);
389
        }
390
        DisposeUtils.dispose((Disposable) tileStruct);
391
        tileStruct = null;
392
        for (Iterator<DownloadedTile> iterator = recentAccededWMTSTiles.values().iterator(); iterator.hasNext();) {
393
            DownloadedTile tile = (DownloadedTile) iterator.next();
394
            DisposeUtils.dispose(tile);
395
            iterator.remove();
396
        }
397
        recentAccededWMTSTiles = null;
398
    }
399

    
400
    protected void removeOlderTile() {
401
        DownloadedTile olderTile = null;
402
        for (Iterator<DownloadedTile> iterator = recentAccededWMTSTiles.values().iterator(); iterator.hasNext();) {
403
            DownloadedTile tile = (DownloadedTile) iterator.next();
404
            if (olderTile == null || tile.getLastAccess() < olderTile.getLastAccess()) {
405
                olderTile = tile;
406
            }
407
        }
408
        if (olderTile != null) {
409
            recentAccededWMTSTiles.remove(olderTile.getKey());
410
            DisposeUtils.dispose(olderTile);
411
        }
412
    }
413

    
414
}