Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / layers / RpfLayer.java
blob88c1ea12f7282dc1338a05e07143267f148f876b
1 /*
2 Copyright (C) 2001, 2006 United States Government as represented by
3 the Administrator of the National Aeronautics and Space Administration.
4 All Rights Reserved.
5 */
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.*;
15 import java.io.*;
16 import java.net.*;
17 import java.nio.*;
18 import java.util.*;
19 import java.util.concurrent.*;
20 import java.util.concurrent.locks.*;
21 import static java.util.logging.Level.*;
23 /**
24 * @author dcollins
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;
69 else
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))
77 return;
78 TextureTile textureTile = (TextureTile) clientObject;
79 disposalQueue.offer(textureTile);
81 });
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);
99 sb.append(": ");
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)
152 this.zone = zone;
153 this.frameNumber = frameNumber;
154 this.hashCode = this.computeHash();
157 private int computeHash()
159 int hash = 0;
160 if (this.zone != null)
161 hash = 29 * hash + this.zone.ordinal();
162 hash = 29 * hash + this.frameNumber;
163 return hash;
166 public boolean equals(Object o)
168 if (this == o)
169 return true;
170 if (o == null || !o.getClass().equals(this.getClass()))
171 return false;
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)
201 if (this == o)
202 return true;
203 if (o == null || !o.getClass().equals(this.getClass()))
204 return false;
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)
232 if (file != null)
233 this.addContents(file);
235 return this.modCount - startModCount;
238 public int addContents(RpfTocFile tocFile)
240 if (tocFile == null)
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)
248 return 0;
249 ArrayList<RpfFrameFileIndexSection.RpfFrameFileIndexRecord> indexTable = indexSection.getFrameFileIndexTable();
250 if (indexTable == null)
251 return 0;
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);
262 catch (Exception e)
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);
281 ++this.modCount;
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)
302 int startIndex = 0;
303 if (str.startsWith("./") || str.startsWith(".\\"))
304 startIndex = 1;
305 else if (!str.startsWith("/") && !str.startsWith("\\"))
306 sb.append(File.separatorChar);
307 int endIndex;
308 if (str.endsWith("/") || str.endsWith("\\"))
309 endIndex = str.length() - 1;
310 else
311 endIndex = str.length();
312 sb.append(str, startIndex, endIndex);
315 if (sb.length() <= 0)
316 return null;
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)
354 if (file != null)
355 this.removeContents(file);
357 return startModCount - this.modCount;
360 public int removeContents(RpfTocFile tocFile)
362 if (tocFile == null)
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)
370 return 0;
371 ArrayList<RpfFrameFileIndexSection.RpfFrameFileIndexRecord> indexTable = indexSection.getFrameFileIndexTable();
372 if (indexTable == null)
373 return 0;
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);
400 --this.modCount;
401 return value != null;
404 // private boolean removeRecord(FrameRecord record)
405 // {
406 // FrameKey key = keyFor(record);
407 // FrameRecord value = this.frameDirectory.remove(key);
408 // --this.modCount;
409 // return value != null;
410 // }
412 private static Sector sectorFor(RpfFrameProperties properties)
414 if (properties == null
415 || properties.zone == null
416 || properties.dataSeries == null)
417 return null;
418 RpfZone.ZoneValues zoneValues = properties.zone.zoneValues(properties.dataSeries);
419 if (properties.frameNumber < 0 || properties.frameNumber > (zoneValues.maximumFrameNumber - 1))
420 return null;
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);
476 if (tile != null)
478 if (isSectorVisible(dc, tile.getSector()))
479 tilesToRender.offer(tile);
481 else
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)
495 Angle tmp = minLat;
496 minLat = maxLat;
497 maxLat = tmp;
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);
553 buffer.rewind();
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);
575 buffer.rewind();
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))
598 return;
600 // Update sector and coverage renderables when frame contents change.
601 if (this.modCount != this.lastModCount)
603 this.updateSector();
604 this.updateCoverage();
605 this.lastModCount = this.modCount;
608 GL gl = dc.getGL();
610 // Coverage tile.
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);
624 finally
626 gl.glPopAttrib();
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.
648 if (drawFrameTiles)
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);
660 finally
662 gl.glPopAttrib();
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()))
716 return false;
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)
754 if (icon == null)
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)
775 if (pixelSize <= 0)
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)
825 this.layer = layer;
826 this.record = record;
829 public boolean equals(Object o)
831 if (this == o)
832 return true;
833 if (o == null || !o.getClass().equals(this.getClass()))
834 return false;
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()
846 return this.buffer;
849 public int getContentLength()
851 return 0;
854 public int getContentLengthRead()
856 return 0;
859 public String getContentType()
861 return null;
864 public long getEndTime()
866 return this.endTime;
869 public String getName()
871 return this.record.filePath;
874 public String getState()
876 return this.state;
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);
892 return true;
894 return false;
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;
910 this.state = 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())
922 return this;
924 if (!this.record.fileLock.tryLock())
926 this.setState(RETRIEVER_STATE_SUCCESSFUL);
927 return this;
931 this.setState(RETRIEVER_STATE_STARTED);
933 if (!this.interrupted())
935 if (isFileResident(this.record.cacheFilePath))
937 this.setState(RETRIEVER_STATE_SUCCESSFUL);
938 return this;
942 if (!this.interrupted())
944 this.setState(RETRIEVER_STATE_CONNECTING);
945 File file = new File(this.record.filePath);
946 if (!file.exists())
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);
958 if (file == null)
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);
974 catch (Exception e)
976 this.setState(RETRIEVER_STATE_ERROR);
977 throw e;
979 finally
981 this.record.fileLock.unlock();
984 return this;
988 private static class ReadTask implements Runnable
990 public final RpfLayer layer;
991 public final FrameRecord record;
993 public ReadTask(RpfLayer layer, FrameRecord record)
995 this.layer = layer;
996 this.record = record;
999 private void deleteCorruptFrame(RpfLayer layer, FrameRecord record)
1001 URL file = WorldWind.dataFileCache().findFile(record.cacheFilePath, false);
1002 if (file != null)
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)
1013 if (this == o)
1014 return true;
1015 if (o == null || !o.getClass().equals(this.getClass()))
1016 return false;
1017 final ReadTask that = (ReadTask) o;
1018 return (this.record != null) ? this.record.equals(that.record) : (that.record == null);
1021 public void run()
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())
1041 return;
1044 if (this.layer.isTileResident(key))
1045 return;
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);
1061 return;
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);
1079 finally
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;
1093 return null;
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))
1123 return;
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.
1145 FrameRecord record;
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.
1159 FrameRecord record;
1160 while (!WorldWind.retrievalService().isFull() && (record = this.downloadQueue.poll()) != null)
1162 WorldWind.retrievalService().runRetriever(new RpfRetriever(this, record));