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