2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
6 package gov
.nasa
.worldwind
.layers
;
8 import com
.sun
.opengl
.util
.*;
9 import com
.sun
.opengl
.util
.texture
.*;
10 import gov
.nasa
.worldwind
.*;
11 import gov
.nasa
.worldwind
.formats
.rpf
.*;
12 import gov
.nasa
.worldwind
.geom
.*;
14 import javax
.media
.opengl
.*;
19 import java
.util
.concurrent
.*;
20 import java
.util
.concurrent
.locks
.*;
21 import static java
.util
.logging
.Level
.*;
25 * @version $Id: RpfLayer.java 1774 2007-05-08 01:03:37Z dcollins $
27 public class RpfLayer
extends AbstractLayer
29 public static final Angle DefaultDeltaLat
= Angle
.fromDegrees(0.05);
30 public static final Angle DefaultDeltaLon
= Angle
.fromDegrees(0.075);
31 private final RpfDataSeries dataSeries
;
32 private final MemoryCache memoryCache
;
33 private Angle deltaLat
;
34 private Angle deltaLon
;
36 public RpfLayer(RpfDataSeries dataSeries
)
38 this(dataSeries
, DefaultDeltaLat
, DefaultDeltaLon
);
41 public RpfLayer(RpfDataSeries dataSeries
, Angle deltaLat
, Angle deltaLon
)
43 this(dataSeries
, deltaLat
, deltaLon
, null);
46 public RpfLayer(RpfDataSeries dataSeries
, Angle deltaLat
, Angle deltaLon
,
47 MemoryCache sharedMemoryCache
)
49 if (dataSeries
== null)
51 String message
= WorldWind
.retrieveErrMsg("nullValue.RpfDataSeriesIsNull");
52 WorldWind
.logger().log(FINE
, message
);
53 throw new IllegalArgumentException(message
);
55 if (deltaLat
== null || deltaLon
== null)
57 String message
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
58 WorldWind
.logger().log(FINE
, message
);
59 throw new IllegalArgumentException(message
);
61 this.dataSeries
= dataSeries
;
62 this.deltaLat
= deltaLat
;
63 this.deltaLon
= deltaLon
;
64 // Initialize the MemoryCache.
65 if (sharedMemoryCache
!= null)
67 this.memoryCache
= sharedMemoryCache
;
71 this.memoryCache
= new BasicMemoryCache();
72 this.memoryCache
.addCacheListener(new MemoryCache
.CacheListener()
74 public void entryRemoved(Object key
, Object clientObject
)
76 if (clientObject
== null || !(clientObject
instanceof TextureTile
))
78 TextureTile textureTile
= (TextureTile
) clientObject
;
79 disposalQueue
.offer(textureTile
);
83 this.updateMemoryCache();
86 public static long estimateMemoryCacheCapacity(RpfDataSeries dataSeries
, Angle deltaLat
, Angle deltaLon
)
88 RpfZone
.ZoneValues zoneValues
= RpfZone
.Zone1
.zoneValues(dataSeries
);
89 long numRows
= (long) Math
.ceil(deltaLat
.divide(zoneValues
.latitudinalFrameExtent
));
90 long numCols
= (long) Math
.ceil(deltaLon
.divide(zoneValues
.longitudinalFrameExtent
));
91 long capacity
= numRows
* numCols
* 4L * 1024L * 1024L;
92 return 32L * (long) Math
.ceil(capacity
/ 32d
);
95 public String
toString()
97 StringBuilder sb
= new StringBuilder();
98 sb
.append(this.dataSeries
.seriesCode
);
100 sb
.append(this.dataSeries
.dataSeries
);
101 return sb
.toString();
104 public Angle
getViewingDeltaLat()
106 return this.deltaLat
;
109 public Angle
getViewingDeltaLon()
111 return this.deltaLon
;
114 public void setViewingDeltaLat(Angle deltaLat
)
116 this.deltaLat
= deltaLat
;
117 this.updateMemoryCache();
120 public void setViewingDeltaLon(Angle deltaLon
)
122 this.deltaLon
= deltaLon
;
123 this.updateMemoryCache();
126 private void updateMemoryCache()
128 long capacity
= estimateMemoryCacheCapacity(this.dataSeries
, this.deltaLat
, this.deltaLon
);
129 if (this.memoryCache
.getCapacity() < capacity
)
130 this.memoryCache
.setCapacity(capacity
);
133 // ============== Frame Directory ======================= //
134 // ============== Frame Directory ======================= //
135 // ============== Frame Directory ======================= //
137 private final static String RPF_OVERVIEW_EXTENSION
= ".OVR";
138 private final Map
<FrameKey
, FrameRecord
> frameDirectory
139 = new HashMap
<FrameKey
, FrameRecord
>();
140 private Sector sector
= Sector
.EMPTY_SECTOR
;
141 private int modCount
= 0;
142 private int lastModCount
= 0;
144 private static class FrameKey
146 public final RpfZone zone
;
147 public final int frameNumber
;
148 private final int hashCode
;
150 public FrameKey(RpfZone zone
, int frameNumber
)
153 this.frameNumber
= frameNumber
;
154 this.hashCode
= this.computeHash();
157 private int computeHash()
160 if (this.zone
!= null)
161 hash
= 29 * hash
+ this.zone
.ordinal();
162 hash
= 29 * hash
+ this.frameNumber
;
166 public boolean equals(Object o
)
170 if (o
== null || !o
.getClass().equals(this.getClass()))
172 final FrameKey that
= (FrameKey
) o
;
173 return (this.zone
== that
.zone
) && (this.frameNumber
== that
.frameNumber
);
176 public int hashCode()
178 return this.hashCode
;
182 private static class FrameRecord
184 public final RpfFrameProperties properties
;
185 public final Sector sector
;
186 public final String filePath
;
187 public final String cacheFilePath
;
188 private boolean corruptCache
= false;
189 final Lock fileLock
= new ReentrantLock();
191 public FrameRecord(RpfFrameProperties properties
, Sector sector
, String filePath
, String cacheFilePath
)
193 this.properties
= properties
;
194 this.sector
= sector
;
195 this.filePath
= filePath
;
196 this.cacheFilePath
= cacheFilePath
;
199 public boolean equals(Object o
)
203 if (o
== null || !o
.getClass().equals(this.getClass()))
205 final FrameRecord that
= (FrameRecord
) o
;
206 return this.filePath
.equals(that
.filePath
) && (this.properties
.equals(that
.properties
));
209 public boolean isCorruptCache()
211 return this.corruptCache
;
214 public void setCorruptCache(boolean corruptCache
)
216 this.corruptCache
= corruptCache
;
220 public int addAll(Collection
<RpfTocFile
> tocFiles
)
222 if (tocFiles
== null)
224 String message
= WorldWind
.retrieveErrMsg("nullValue.CollectionIsNull");
225 WorldWind
.logger().log(FINE
, message
);
226 throw new IllegalArgumentException(message
);
228 int startModCount
= this.modCount
;
229 // Fill frame directory with contents in 'tocFiles'.
230 for (RpfTocFile file
: tocFiles
)
233 this.addContents(file
);
235 return this.modCount
- startModCount
;
238 public int addContents(RpfTocFile tocFile
)
242 String message
= WorldWind
.retrieveErrMsg("nullValue.RpfTocFileIsNull");
243 WorldWind
.logger().log(FINE
, message
);
244 throw new IllegalArgumentException(message
);
246 RpfFrameFileIndexSection indexSection
= tocFile
.getFrameFileIndexSection();
247 if (indexSection
== null)
249 ArrayList
<RpfFrameFileIndexSection
.RpfFrameFileIndexRecord
> indexTable
= indexSection
.getFrameFileIndexTable();
250 if (indexTable
== null)
252 int startModCount
= this.modCount
;
253 for (RpfFrameFileIndexSection
.RpfFrameFileIndexRecord indexRecord
: indexTable
)
255 if (!indexRecord
.getFrameFileName().toUpperCase().endsWith(RPF_OVERVIEW_EXTENSION
))
257 FrameRecord record
= null;
260 record
= createRecord(tocFile
, indexRecord
);
264 String message
= WorldWind
.retrieveErrMsg("layers.RpfLayer.ExceptionParsingFileName")
265 + indexRecord
.getFrameFileName();
266 WorldWind
.logger().log(FINE
, message
, e
);
268 if (record
!= null && this.dataSeries
== record
.properties
.dataSeries
)
270 this.addRecord(record
);
274 return this.modCount
- startModCount
;
277 private void addRecord(FrameRecord record
)
279 FrameKey key
= keyFor(record
);
280 this.frameDirectory
.put(key
, record
);
284 private static String
cachePathFor(RpfFrameProperties properties
)
286 StringBuilder sb
= new StringBuilder();
287 sb
.append("Earth").append(File
.separatorChar
);
288 sb
.append("RPF").append(File
.separatorChar
);
289 sb
.append(properties
.dataSeries
.seriesCode
).append(File
.separatorChar
);
290 sb
.append(properties
.zone
.zoneCode
).append(File
.separatorChar
);
291 sb
.append(RpfFrameFilenameUtil
.filenameFor(properties
));
292 return sb
.toString();
295 private static String
createAbsolutePath(String
... pathElem
)
297 StringBuilder sb
= new StringBuilder();
298 for (String str
: pathElem
)
300 if (str
!= null && str
.length() > 0)
303 if (str
.startsWith("./") || str
.startsWith(".\\"))
305 else if (!str
.startsWith("/") && !str
.startsWith("\\"))
306 sb
.append(File
.separatorChar
);
308 if (str
.endsWith("/") || str
.endsWith("\\"))
309 endIndex
= str
.length() - 1;
311 endIndex
= str
.length();
312 sb
.append(str
, startIndex
, endIndex
);
315 if (sb
.length() <= 0)
317 return sb
.toString();
320 private static FrameRecord
createRecord(RpfTocFile tocFile
,
321 RpfFrameFileIndexSection
.RpfFrameFileIndexRecord indexRecord
)
323 RpfFrameProperties frameProps
= RpfFrameFilenameUtil
.parseFilename(indexRecord
.getFrameFileName());
324 Sector sector
= sectorFor(frameProps
);
325 String filePath
= createAbsolutePath(tocFile
.getFile().getParentFile().getAbsolutePath(),
326 indexRecord
.getPathname(), indexRecord
.getFrameFileName());
327 String cachePath
= cachePathFor(frameProps
);
328 if (frameProps
== null || sector
== null || filePath
== null || cachePath
== null)
330 String message
= WorldWind
.retrieveErrMsg("layers.RpfLayer.BadFrameInput") + indexRecord
.getFrameFileName();
331 WorldWind
.logger().log(FINE
, message
);
332 throw new WWRuntimeException(message
);
334 return new FrameRecord(frameProps
, sector
, filePath
, cachePath
);
337 private static FrameKey
keyFor(FrameRecord record
)
339 return new FrameKey(record
.properties
.zone
, record
.properties
.frameNumber
);
342 public int removeAll(Collection
<RpfTocFile
> tocFiles
)
344 if (tocFiles
== null)
346 String message
= WorldWind
.retrieveErrMsg("nullValue.CollectionIsNull");
347 WorldWind
.logger().log(FINE
, message
);
348 throw new IllegalArgumentException(message
);
350 int startModCount
= this.modCount
;
351 // Fill frame directory with contents in 'tocFiles'.
352 for (RpfTocFile file
: tocFiles
)
355 this.removeContents(file
);
357 return startModCount
- this.modCount
;
360 public int removeContents(RpfTocFile tocFile
)
364 String message
= WorldWind
.retrieveErrMsg("nullValue.RpfTocFileIsNull");
365 WorldWind
.logger().log(FINE
, message
);
366 throw new IllegalArgumentException(message
);
368 RpfFrameFileIndexSection indexSection
= tocFile
.getFrameFileIndexSection();
369 if (indexSection
== null)
371 ArrayList
<RpfFrameFileIndexSection
.RpfFrameFileIndexRecord
> indexTable
= indexSection
.getFrameFileIndexTable();
372 if (indexTable
== null)
374 int startModCount
= this.modCount
;
375 for (RpfFrameFileIndexSection
.RpfFrameFileIndexRecord indexRecord
: indexTable
)
377 if (!indexRecord
.getFrameFileName().toUpperCase().endsWith(RPF_OVERVIEW_EXTENSION
))
379 RpfFrameProperties frameProps
= null;
382 frameProps
= RpfFrameFilenameUtil
.parseFilename(indexRecord
.getFrameFileName());
384 catch (IllegalArgumentException e
)
386 String message
= WorldWind
.retrieveErrMsg("layers.RpfLayer.ExceptionParsingFileName")
387 + indexRecord
.getFrameFileName();
388 WorldWind
.logger().log(FINE
, message
, e
);
390 if (frameProps
!= null && this.dataSeries
== frameProps
.dataSeries
)
391 this.removeKey(new FrameKey(frameProps
.zone
, frameProps
.frameNumber
));
394 return startModCount
- this.modCount
;
397 private boolean removeKey(FrameKey key
)
399 FrameRecord value
= this.frameDirectory
.remove(key
);
401 return value
!= null;
404 // private boolean removeRecord(FrameRecord record)
406 // FrameKey key = keyFor(record);
407 // FrameRecord value = this.frameDirectory.remove(key);
409 // return value != null;
412 private static Sector
sectorFor(RpfFrameProperties properties
)
414 if (properties
== null
415 || properties
.zone
== null
416 || properties
.dataSeries
== null)
418 RpfZone
.ZoneValues zoneValues
= properties
.zone
.zoneValues(properties
.dataSeries
);
419 if (properties
.frameNumber
< 0 || properties
.frameNumber
> (zoneValues
.maximumFrameNumber
- 1))
421 return zoneValues
.frameExtent(properties
.frameNumber
);
424 private void updateSector()
426 Sector newSector
= null;
427 for (FrameRecord record
: this.frameDirectory
.values())
429 if (record
.sector
!= null)
430 newSector
= (newSector
!= null) ? newSector
.union(record
.sector
) : record
.sector
;
432 this.sector
= newSector
;
435 // ============== Tile Assembly ======================= //
436 // ============== Tile Assembly ======================= //
437 // ============== Tile Assembly ======================= //
439 private final Queue
<TextureTile
> assemblyQueue
= new LinkedList
<TextureTile
>();
440 private final Queue
<FrameRecord
> assemblyRequestQueue
= new LinkedList
<FrameRecord
>();
442 private void assembleFrameTiles(DrawContext dc
, RpfDataSeries dataSeries
, Sector viewingSector
,
443 Queue
<TextureTile
> tilesToRender
, Queue
<FrameRecord
> framesToRequest
)
445 for (RpfZone zone
: RpfZone
.values())
447 RpfZone
.ZoneValues zoneValues
= zone
.zoneValues(dataSeries
);
448 Sector sector
= zoneValues
.extent
.intersection(viewingSector
);
449 if (sector
!= null && isSectorVisible(dc
, sector
))
450 this.assembleZoneTiles(dc
, zoneValues
, sector
, tilesToRender
, framesToRequest
);
454 private void assembleZoneTiles(DrawContext dc
, RpfZone
.ZoneValues zoneValues
, Sector sector
,
455 Queue
<TextureTile
> tilesToRender
, Queue
<FrameRecord
> framesToRequest
)
457 int startRow
= zoneValues
.frameRowFromLatitude(sector
.getMinLatitude());
458 int endRow
= zoneValues
.frameRowFromLatitude(sector
.getMaxLatitude());
459 int startCol
= zoneValues
.frameColumnFromLongitude(sector
.getMinLongitude());
460 int endCol
= zoneValues
.frameColumnFromLongitude(sector
.getMaxLongitude());
462 for (int row
= startRow
; row
<= endRow
; row
++)
464 for (int col
= startCol
; col
<= endCol
; col
++)
466 int frameNum
= zoneValues
.frameNumber(row
, col
);
467 getOrRequestTile(dc
, new FrameKey(zoneValues
.zone
, frameNum
), tilesToRender
, framesToRequest
);
472 private void getOrRequestTile(DrawContext dc
, FrameKey key
, Queue
<TextureTile
> tilesToRender
,
473 Queue
<FrameRecord
> framesToRequest
)
475 TextureTile tile
= this.getTile(key
);
478 if (isSectorVisible(dc
, tile
.getSector()))
479 tilesToRender
.offer(tile
);
483 FrameRecord record
= this.frameDirectory
.get(key
);
484 if (record
!= null && isSectorVisible(dc
, record
.sector
))
485 framesToRequest
.offer(record
);
489 private static Sector
[] normalizeSector(Sector sector
)
491 Angle minLat
= clampAngle(sector
.getMinLatitude(), Angle
.NEG90
, Angle
.POS90
);
492 Angle maxLat
= clampAngle(sector
.getMaxLatitude(), Angle
.NEG90
, Angle
.POS90
);
493 if (maxLat
.degrees
< minLat
.degrees
)
500 Angle minLon
= normalizeAngle(sector
.getMinLongitude(), Angle
.NEG180
, Angle
.POS180
);
501 Angle maxLon
= normalizeAngle(sector
.getMaxLongitude(), Angle
.NEG180
, Angle
.POS180
);
502 if (maxLon
.degrees
< minLon
.degrees
)
504 return new Sector
[] {
505 new Sector(minLat
, maxLat
, minLon
, Angle
.POS180
),
506 new Sector(minLat
, maxLat
, Angle
.NEG180
, maxLon
),
510 return new Sector
[] {new Sector(minLat
, maxLat
, minLon
, maxLon
)};
513 private static Sector
createViewSector(Angle centerLat
, Angle centerLon
, Angle deltaLat
, Angle deltaLon
)
515 return new Sector(centerLat
.subtract(deltaLat
), centerLat
.add(deltaLat
),
516 centerLon
.subtract(deltaLon
), centerLon
.add(deltaLon
));
519 private static Angle
clampAngle(Angle angle
, Angle min
, Angle max
)
521 return (angle
.degrees
< min
.degrees
) ? min
: ((angle
.degrees
> max
.degrees
) ? max
: angle
);
524 private static Angle
normalizeAngle(Angle angle
, Angle min
, Angle max
)
526 Angle range
= max
.subtract(min
);
527 return (angle
.degrees
< min
.degrees
) ?
528 angle
.add(range
) : ((angle
.degrees
> max
.degrees
) ? angle
.subtract(range
) : angle
);
531 // ============== Rendering ======================= //
532 // ============== Rendering ======================= //
533 // ============== Rendering ======================= //
535 private static final BlockingQueue
<Disposable
> disposalQueue
= new LinkedBlockingQueue
<Disposable
>();
536 // private final List<TextureTile> tileQueue = new ArrayList<TextureTile>();
537 private final SurfaceTileRenderer tileRenderer
= new SurfaceTileRenderer();
538 private final IconRenderer iconRenderer
= new IconRenderer();
539 private TextureTile coverageTile
;
540 private WWIcon coverageIcon
;
541 private int tileGridDrawThreshold
= 30;
542 private boolean drawCoverage
= true;
543 private boolean drawCoverageIcon
= true;
545 private static TextureData
createCoverageTextureData(Sector sector
, Collection
<FrameRecord
> records
,
546 int width
, int height
, int fgColor
, int bgColor
)
548 IntBuffer buffer
= BufferUtil
.newIntBuffer(width
* height
);
549 for (int i
= 0; i
< width
* height
; i
++)
551 buffer
.put(i
, bgColor
);
555 Angle latWidth
= sector
.getMaxLatitude().subtract(sector
.getMinLatitude());
556 Angle lonWidth
= sector
.getMaxLongitude().subtract(sector
.getMinLongitude());
557 for (FrameRecord record
: records
)
559 int x0
= (int) Math
.round((width
- 1)
560 * record
.sector
.getMinLongitude().subtract(sector
.getMinLongitude()).divide(lonWidth
));
561 int y0
= (int) Math
.round((height
- 1)
562 * record
.sector
.getMinLatitude().subtract(sector
.getMinLatitude()).divide(latWidth
));
563 int x1
= (int) Math
.round((width
- 1)
564 * record
.sector
.getMaxLongitude().subtract(sector
.getMinLongitude()).divide(lonWidth
));
565 int y1
= (int) Math
.round((width
- 1)
566 * record
.sector
.getMaxLatitude().subtract(sector
.getMinLatitude()).divide(latWidth
));
567 for (int y
= y0
; y
<= y1
; y
++)
569 for (int x
= x0
; x
<= x1
; x
++)
571 buffer
.put(x
+ y
* width
, fgColor
);
576 return new TextureData(GL
.GL_RGBA
, width
, height
, 0, GL
.GL_RGBA
, GL
.GL_UNSIGNED_INT_8_8_8_8
,
577 false, false, false, buffer
, null);
580 public void dispose()
582 if (this.tileRenderer
!= null)
583 disposalQueue
.offer(this.tileRenderer
);
584 if (this.iconRenderer
!= null)
585 disposalQueue
.offer(this.iconRenderer
);
586 if (this.coverageTile
!= null)
587 disposalQueue
.offer(this.coverageTile
);
588 this.coverageTile
= null;
589 processDisposables();
592 protected void doRender(DrawContext dc
)
594 // Process disposable queue.
595 processDisposables();
597 if (!isSectorVisible(dc
, this.sector
))
600 // Update sector and coverage renderables when frame contents change.
601 if (this.modCount
!= this.lastModCount
)
604 this.updateCoverage();
605 this.lastModCount
= this.modCount
;
611 if (this.drawCoverage
&& this.coverageTile
!= null)
613 int attribBits
= GL
.GL_ENABLE_BIT
| GL
.GL_COLOR_BUFFER_BIT
| GL
.GL_POLYGON_BIT
;
614 gl
.glPushAttrib(attribBits
);
617 gl
.glEnable(GL
.GL_BLEND
);
618 gl
.glEnable(GL
.GL_CULL_FACE
);
619 gl
.glBlendFunc(GL
.GL_SRC_ALPHA
, GL
.GL_ONE_MINUS_SRC_ALPHA
);
620 gl
.glCullFace(GL
.GL_BACK
);
621 gl
.glPolygonMode(GL
.GL_FRONT
, GL
.GL_FILL
);
622 this.tileRenderer
.renderTile(dc
, this.coverageTile
);
629 // Assemble frame tiles.
630 this.assemblyQueue
.clear();
631 this.assemblyRequestQueue
.clear();
632 Position viewPosition
= dc
.getView().getPosition();
633 Sector viewingSector
= createViewSector(viewPosition
.getLatitude(), viewPosition
.getLongitude(),
634 this.deltaLat
, this.deltaLon
);
635 for (Sector sector
: normalizeSector(viewingSector
))
637 this.assembleFrameTiles(dc
, this.dataSeries
, sector
, this.assemblyQueue
, this.assemblyRequestQueue
);
639 Sector drawSector
= null;
640 for (TextureTile tile
: this.assemblyQueue
)
642 drawSector
= (drawSector
!= null) ? drawSector
.union(tile
.getSector()) : tile
.getSector();
645 boolean drawFrameTiles
= this.tileGridDrawThreshold
<= pixelSizeOfSector(dc
, viewingSector
)
646 || (drawSector
!= null && this.tileGridDrawThreshold
<= pixelSizeOfSector(dc
, drawSector
));
647 // Render frame tiles.
650 this.requestAllFrames(this.readQueue
, this.assemblyRequestQueue
);
651 int attribBits
= GL
.GL_ENABLE_BIT
| GL
.GL_POLYGON_BIT
;
652 gl
.glPushAttrib(attribBits
);
655 gl
.glEnable(GL
.GL_CULL_FACE
);
656 gl
.glCullFace(GL
.GL_BACK
);
657 gl
.glPolygonMode(GL
.GL_FRONT
, GL
.GL_FILL
);
658 this.tileRenderer
.renderTiles(dc
, this.assemblyQueue
);
665 // Render coverage icon.
666 else if (this.drawCoverageIcon
&& this.coverageIcon
!= null)
668 LatLon centroid
= (drawSector
!= null) ? drawSector
.getCentroid() : this.sector
.getCentroid();
669 this.coverageIcon
.setPosition(new Position(centroid
.getLatitude(), centroid
.getLongitude(), 0));
670 this.iconRenderer
.render(dc
, this.coverageIcon
, null);
673 // Process request queue.
674 this.sendRequests(this.assemblyRequestQueue
.size());
677 public WWIcon
getCoverageIcon()
679 return this.coverageIcon
;
682 public int getTileGridDrawThreshold()
684 return this.tileGridDrawThreshold
;
687 private static void initializeFrameTexture(Texture texture
)
689 texture
.setTexParameteri(GL
.GL_TEXTURE_MIN_FILTER
, GL
.GL_LINEAR
);
690 texture
.setTexParameteri(GL
.GL_TEXTURE_MAG_FILTER
, GL
.GL_LINEAR
);
691 texture
.setTexParameteri(GL
.GL_TEXTURE_WRAP_S
, GL
.GL_CLAMP_TO_EDGE
);
692 texture
.setTexParameteri(GL
.GL_TEXTURE_WRAP_T
, GL
.GL_CLAMP_TO_EDGE
);
695 private static void initializeOtherTexture(Texture texture
)
697 texture
.setTexParameteri(GL
.GL_TEXTURE_MIN_FILTER
, GL
.GL_NEAREST
);
698 texture
.setTexParameteri(GL
.GL_TEXTURE_MAG_FILTER
, GL
.GL_NEAREST
);
699 texture
.setTexParameteri(GL
.GL_TEXTURE_WRAP_S
, GL
.GL_CLAMP_TO_EDGE
);
700 texture
.setTexParameteri(GL
.GL_TEXTURE_WRAP_T
, GL
.GL_CLAMP_TO_EDGE
);
703 public boolean isDrawCoverage()
705 return this.drawCoverage
;
708 public boolean isDrawCoverageIcon()
710 return this.drawCoverageIcon
;
713 private boolean isSectorVisible(DrawContext dc
, Sector sector
)
715 if (dc
.getVisibleSector() != null && !sector
.intersects(dc
.getVisibleSector()))
718 Extent e
= Sector
.computeBoundingCylinder(dc
.getGlobe(), dc
.getVerticalExaggeration(), sector
);
719 return e
.intersects(dc
.getView().getFrustumInModelCoordinates());
722 private static int pixelSizeOfSector(DrawContext dc
, Sector sector
)
724 LatLon centroid
= sector
.getCentroid();
725 Globe globe
= dc
.getGlobe();
726 Point centroidPoint
= globe
.computePointFromPosition(centroid
.getLatitude(), centroid
.getLongitude(), 0);
727 Point minPoint
= globe
.computePointFromPosition(sector
.getMinLatitude(), sector
.getMinLongitude(), 0);
728 Point maxPoint
= globe
.computePointFromPosition(sector
.getMaxLatitude(), sector
.getMaxLongitude(), 0);
729 double distanceToEye
= centroidPoint
.distanceTo(dc
.getView().getEyePoint());
730 double sectorSize
= minPoint
.distanceTo(maxPoint
);
731 double pixelSize
= dc
.getView().computePixelSizeAtDistance(distanceToEye
);
732 return (int) Math
.round(sectorSize
/ pixelSize
);
735 private static void processDisposables()
737 Disposable disposable
;
738 while ((disposable
= disposalQueue
.poll()) != null)
740 disposable
.dispose();
744 private static int rgbaInt(int r
, int g
, int b
, int a
)
746 r
= (int) (r
* (a
/ 255d
));
747 g
= (int) (g
* (a
/ 255d
));
748 b
= (int) (b
* (a
/ 255d
));
749 return ((0xFF & r
) << 24) + ((0xFF & g
) << 16) + ((0xFF & b
) << 8) + (0xFF & a
);
752 public void setCoverageIcon(WWIcon icon
)
756 String message
= WorldWind
.retrieveErrMsg("nullValue.Icon");
757 WorldWind
.logger().log(FINE
, message
);
758 throw new IllegalArgumentException(message
);
760 this.coverageIcon
= icon
;
763 public void setDrawCoverage(boolean drawCoverage
)
765 this.drawCoverage
= drawCoverage
;
768 public void setDrawCoverageIcon(boolean drawCoverageIcon
)
770 this.drawCoverageIcon
= drawCoverageIcon
;
773 public void setTileGridDrawThreshold(int pixelSize
)
777 String message
= WorldWind
.retrieveErrMsg("generic.ValueOutOfRange") + String
.valueOf(pixelSize
);
778 WorldWind
.logger().log(FINE
, message
);
779 throw new IllegalArgumentException(message
);
781 this.tileGridDrawThreshold
= pixelSize
;
784 private void updateCoverage()
786 if (this.coverageTile
!= null)
787 disposalQueue
.offer(this.coverageTile
);
788 TextureData textureData
= createCoverageTextureData(this.sector
, this.frameDirectory
.values(), 1024, 1024,
789 rgbaInt(255, 0, 0, 102), rgbaInt(0, 0, 0, 0));
790 this.coverageTile
= new TextureTile(this.sector
)
792 public void initializeTexture(DrawContext dc
)
794 if (this.getTexture() == null)
796 Texture tex
= TextureIO
.newTexture(this.getTextureData());
797 initializeOtherTexture(tex
);
798 this.setTexture(tex
);
802 this.coverageTile
.setTextureData(textureData
);
805 // ============== Image Reading and Conversion ======================= //
806 // ============== Image Reading and Conversion ======================= //
807 // ============== Image Reading and Conversion ======================= //
809 private final LinkedList
<FrameRecord
> downloadQueue
= new LinkedList
<FrameRecord
>();
810 private final LinkedList
<FrameRecord
> readQueue
= new LinkedList
<FrameRecord
>();
812 private static class RpfRetriever
extends WWObjectImpl
implements Retriever
814 private final RpfLayer layer
;
815 private final FrameRecord record
;
816 private volatile RpfImageFile rpfImageFile
;
817 private volatile ByteBuffer buffer
;
818 private volatile String state
= RETRIEVER_STATE_NOT_STARTED
;
819 private long submitTime
;
820 private long beginTime
;
821 private long endTime
;
823 public RpfRetriever(RpfLayer layer
, FrameRecord record
)
826 this.record
= record
;
829 public boolean equals(Object o
)
833 if (o
== null || !o
.getClass().equals(this.getClass()))
835 final RpfRetriever that
= (RpfRetriever
) o
;
836 return this.record
.equals(that
.record
);
839 public long getBeginTime()
841 return this.beginTime
;
844 public ByteBuffer
getBuffer()
849 public int getContentLength()
854 public int getContentLengthRead()
859 public String
getContentType()
864 public long getEndTime()
869 public String
getName()
871 return this.record
.filePath
;
874 public String
getState()
879 public long getSubmitTime()
881 return this.submitTime
;
884 private boolean interrupted()
886 if (Thread
.currentThread().isInterrupted())
888 this.setState(RETRIEVER_STATE_INTERRUPTED
);
889 String message
= WorldWind
.retrieveErrMsg("layers.RpfLayer.DownloadInterrupted")
890 + this.record
.filePath
;
891 WorldWind
.logger().log(FINER
, message
);
897 public void setBeginTime(long beginTime
)
899 this.beginTime
= beginTime
;
902 public void setEndTime(long endTime
)
904 this.endTime
= endTime
;
907 private void setState(String state
)
909 String oldState
= this.state
;
911 this.firePropertyChange(AVKey
.RETRIEVER_STATE
, oldState
, this.state
);
914 public void setSubmitTime(long submitTime
)
916 this.submitTime
= submitTime
;
919 public Retriever
call() throws Exception
921 if (this.interrupted())
924 if (!this.record
.fileLock
.tryLock())
926 this.setState(RETRIEVER_STATE_SUCCESSFUL
);
931 this.setState(RETRIEVER_STATE_STARTED
);
933 if (!this.interrupted())
935 if (isFileResident(this.record
.cacheFilePath
))
937 this.setState(RETRIEVER_STATE_SUCCESSFUL
);
942 if (!this.interrupted())
944 this.setState(RETRIEVER_STATE_CONNECTING
);
945 File file
= new File(this.record
.filePath
);
948 String message
= WorldWind
.retrieveErrMsg("generic.fileNotFound") + this.record
.filePath
;
949 throw new IOException(message
);
951 this.rpfImageFile
= RpfImageFile
.load(file
);
954 if (!this.interrupted())
956 this.setState(RETRIEVER_STATE_READING
);
957 File file
= WorldWind
.dataFileCache().newFile(this.record
.cacheFilePath
);
960 String message
= WorldWind
.retrieveErrMsg("generic.CantCreateCacheFile")
961 + this.record
.cacheFilePath
;
962 throw new IOException(message
);
964 this.buffer
= this.rpfImageFile
.getImageAsDdsTexture();
965 WWIO
.saveBuffer(this.buffer
, file
);
968 if (!this.interrupted())
970 this.setState(RETRIEVER_STATE_SUCCESSFUL
);
971 this.layer
.firePropertyChange(AVKey
.LAYER
, null, this.layer
);
976 this.setState(RETRIEVER_STATE_ERROR
);
981 this.record
.fileLock
.unlock();
988 private static class ReadTask
implements Runnable
990 public final RpfLayer layer
;
991 public final FrameRecord record
;
993 public ReadTask(RpfLayer layer
, FrameRecord record
)
996 this.record
= record
;
999 private void deleteCorruptFrame(RpfLayer layer
, FrameRecord record
)
1001 URL file
= WorldWind
.dataFileCache().findFile(record
.cacheFilePath
, false);
1003 WorldWind
.dataFileCache().removeFile(file
);
1004 record
.setCorruptCache(false);
1005 layer
.firePropertyChange(AVKey
.LAYER
, null, layer
);
1006 String message
= WorldWind
.retrieveErrMsg("generic.DeletedCorruptDataFile")
1007 + ((file
!= null) ? file
.getFile() : "null");
1008 WorldWind
.logger().log(FINE
, message
);
1011 public boolean equals(Object o
)
1015 if (o
== null || !o
.getClass().equals(this.getClass()))
1017 final ReadTask that
= (ReadTask
) o
;
1018 return (this.record
!= null) ?
this.record
.equals(that
.record
) : (that
.record
== null);
1023 FrameKey key
= keyFor(this.record
);
1024 if (!this.layer
.isTileResident(key
))
1026 this.readFrame(key
, this.record
);
1027 this.layer
.firePropertyChange(AVKey
.LAYER
, null, this.layer
);
1031 public void readFrame(FrameKey key
, FrameRecord record
)
1033 URL dataFileURL
= WorldWind
.dataFileCache().findFile(record
.cacheFilePath
, false);
1034 if (dataFileURL
== null)
1036 this.layer
.requestFrame(this.layer
.downloadQueue
, this.record
);
1037 return; // Return when cached file does not exist.
1040 if (!record
.fileLock
.tryLock())
1044 if (this.layer
.isTileResident(key
))
1047 TextureData textureData
= null;
1050 textureData
= TextureIO
.newTextureData(dataFileURL
, false, "dds");
1052 catch (IOException e
)
1054 String message
= WorldWind
.retrieveErrMsg("generic.TextureIOException") + record
.cacheFilePath
;
1055 WorldWind
.logger().log(FINE
, message
, e
);
1058 if (textureData
== null)
1060 this.deleteCorruptFrame(this.layer
, this.record
);
1064 TextureTile textureTile
= new TextureTile(record
.sector
)
1066 public void initializeTexture(DrawContext dc
)
1068 if (this.getTexture() == null)
1070 Texture tex
= TextureIO
.newTexture(this.getTextureData());
1071 initializeFrameTexture(tex
);
1072 this.setTexture(tex
);
1076 textureTile
.setTextureData(textureData
);
1077 this.layer
.makeTileResident(key
, textureTile
);
1081 record
.fileLock
.unlock();
1086 private TextureTile
getTile(FrameKey key
)
1088 synchronized (this.memoryCache
)
1090 Object obj
= this.memoryCache
.getObject(key
);
1091 if (obj
!= null && obj
instanceof TextureTile
)
1092 return (TextureTile
) obj
;
1097 private static boolean isFileResident(String fileName
)
1099 return WorldWind
.dataFileCache().findFile(fileName
, false) != null;
1102 private boolean isTileResident(FrameKey key
)
1104 synchronized (this.memoryCache
)
1106 return this.memoryCache
.getObject(key
) != null;
1110 private void makeTileResident(FrameKey key
, TextureTile tile
)
1112 synchronized (this.memoryCache
)
1114 this.memoryCache
.add(key
, tile
);
1118 private void requestFrame(LinkedList
<FrameRecord
> queue
, FrameRecord record
)
1120 synchronized (queue
)
1122 if (queue
.contains(record
))
1124 queue
.addFirst(record
);
1128 private void requestAllFrames(LinkedList
<FrameRecord
> queue
, Collection
<FrameRecord
> frameRecords
)
1130 for (FrameRecord record
: frameRecords
)
1132 this.requestFrame(queue
, record
);
1136 private void sendRequests(int maxRequests
)
1138 synchronized (this.readQueue
)
1140 while (this.readQueue
.size() > maxRequests
)
1142 this.readQueue
.removeLast();
1144 // Send threaded read tasks.
1146 while (!WorldWind
.threadedTaskService().isFull() && (record
= this.readQueue
.poll()) != null)
1148 WorldWind
.threadedTaskService().addTask(new ReadTask(this, record
));
1152 synchronized (this.downloadQueue
)
1154 while (this.downloadQueue
.size() > maxRequests
)
1156 this.downloadQueue
.removeLast();
1158 // Send retriever tasks.
1160 while (!WorldWind
.retrievalService().isFull() && (record
= this.downloadQueue
.poll()) != null)
1162 WorldWind
.retrievalService().runRetriever(new RpfRetriever(this, record
));