Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / layers / PlaceNameLayer.java
blobc0a80af82ee81c1ef6b244148cbf49238951717e
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 gov.nasa.worldwind.*;
10 import gov.nasa.worldwind.geom.*;
12 import java.awt.*;
13 import java.util.*;
14 import java.util.List;
15 import java.util.Queue;
16 import java.util.concurrent.*;
17 import java.util.logging.Level;
19 /**
20 * @author Paul Collins
21 * @version $Id: PlaceNameLayer.java 2012 2007-06-13 06:19:51Z dcollins $
23 public class PlaceNameLayer extends AbstractLayer
25 private final PlaceNameServiceSet placeNameServiceSet;
26 private final List<Tile[]> tiles = new ArrayList<Tile[]>();
28 /**
29 * @param placeNameServiceSet the set of PlaceNameService objects that PlaceNameLayer will render.
30 * @throws IllegalArgumentException if <code>placeNameServiceSet</code> is null
32 public PlaceNameLayer(PlaceNameServiceSet placeNameServiceSet)
34 if (placeNameServiceSet == null)
36 String message = WorldWind.retrieveErrMsg("nullValue.PlaceNameServiceSetIsNull");
37 WorldWind.logger().log(Level.FINE, message);
38 throw new IllegalArgumentException(message);
41 this.placeNameServiceSet = placeNameServiceSet.deepCopy();
42 for (int i = 0; i < this.placeNameServiceSet.getServiceCount(); i++)
44 tiles.add(i, buildTiles(this.placeNameServiceSet.getService(i)));
48 public final PlaceNameServiceSet getPlaceNameServiceSet()
50 return this.placeNameServiceSet;
53 // ============== Tile Assembly ======================= //
54 // ============== Tile Assembly ======================= //
55 // ============== Tile Assembly ======================= //
57 private static class Tile
59 final PlaceNameService placeNameService;
60 final Sector sector;
61 final int row;
62 final int column;
63 final int hash;
64 // Computed data.
65 String fileCachePath = null;
66 Extent extent = null;
67 double extentVerticalExaggeration = Double.MIN_VALUE;
69 static int computeRow(Angle delta, Angle latitude)
71 if (delta == null || latitude == null)
73 String msg = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
74 WorldWind.logger().log(Level.FINE, msg);
75 throw new IllegalArgumentException(msg);
77 return (int) ((latitude.getDegrees() + 90d) / delta.getDegrees());
80 static int computeColumn(Angle delta, Angle longitude)
82 if (delta == null || longitude == null)
84 String msg = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
85 WorldWind.logger().log(Level.FINE, msg);
86 throw new IllegalArgumentException(msg);
88 return (int) ((longitude.getDegrees() + 180d) / delta.getDegrees());
91 static Angle computeRowLatitude(int row, Angle delta)
93 if (delta == null)
95 String msg = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
96 WorldWind.logger().log(Level.FINE, msg);
97 throw new IllegalArgumentException(msg);
99 return Angle.fromDegrees(-90d + delta.getDegrees() * row);
102 static Angle computeColumnLongitude(int column, Angle delta)
104 if (delta == null)
106 String msg = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
107 WorldWind.logger().log(Level.FINE, msg);
108 throw new IllegalArgumentException(msg);
110 return Angle.fromDegrees(-180 + delta.getDegrees() * column);
113 Tile(PlaceNameService placeNameService, Sector sector, int row, int column)
115 this.placeNameService = placeNameService;
116 this.sector = sector;
117 this.row = row;
118 this.column = column;
119 this.hash = this.computeHash();
122 int computeHash()
124 return this.getFileCachePath() != null ? this.getFileCachePath().hashCode() : 0;
127 @Override
128 public boolean equals(Object o)
130 if (this == o)
131 return true;
132 if (o == null || this.getClass() != o.getClass())
133 return false;
135 final Tile other = (Tile) o;
137 return this.getFileCachePath() != null ? !this.getFileCachePath().equals(other.getFileCachePath()) :
138 other.getFileCachePath() != null;
141 Extent getExtent(DrawContext dc)
143 if (dc == null)
145 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
146 WorldWind.logger().log(Level.FINE, message);
147 throw new IllegalArgumentException(message);
150 if (this.extent == null || this.extentVerticalExaggeration != dc.getVerticalExaggeration())
152 this.extentVerticalExaggeration = dc.getVerticalExaggeration();
153 this.extent = Sector.computeBoundingCylinder(dc.getGlobe(), this.extentVerticalExaggeration,
154 this.sector);
157 return extent;
160 String getFileCachePath()
162 if (this.fileCachePath == null)
163 this.fileCachePath = this.placeNameService.createFileCachePathFromTile(this.row, this.column);
165 return this.fileCachePath;
168 PlaceNameService getPlaceNameService()
170 return placeNameService;
173 java.net.URL getRequestURL() throws java.net.MalformedURLException
175 return this.placeNameService.createServiceURLFromSector(this.sector);
178 Sector getSector()
180 return sector;
183 public int hashCode()
185 return this.hash;
189 private Tile[] buildTiles(PlaceNameService placeNameService)
191 final Sector sector = placeNameService.getSector();
192 final Angle dLat = placeNameService.getTileDelta().getLatitude();
193 final Angle dLon = placeNameService.getTileDelta().getLongitude();
195 // Determine the row and column offset from the global tiling origin for the southwest tile corner
196 int firstRow = Tile.computeRow(dLat, sector.getMinLatitude());
197 int firstCol = Tile.computeColumn(dLon, sector.getMinLongitude());
198 int lastRow = Tile.computeRow(dLat, sector.getMaxLatitude().subtract(dLat));
199 int lastCol = Tile.computeColumn(dLon, sector.getMaxLongitude().subtract(dLon));
201 int nLatTiles = lastRow - firstRow + 1;
202 int nLonTiles = lastCol - firstCol + 1;
204 Tile[] tiles = new Tile[nLatTiles * nLonTiles];
206 Angle p1 = Tile.computeRowLatitude(firstRow, dLat);
207 for (int row = firstRow; row <= lastRow; row++)
209 Angle p2;
210 p2 = p1.add(dLat);
212 Angle t1 = Tile.computeColumnLongitude(firstCol, dLon);
213 for (int col = firstCol; col <= lastCol; col++)
215 Angle t2;
216 t2 = t1.add(dLon);
218 tiles[col + row * nLonTiles] = new Tile(placeNameService, new Sector(p1, p2, t1, t2), row, col);
219 t1 = t2;
221 p1 = p2;
224 return tiles;
227 // ============== Place Name Data Structures ======================= //
228 // ============== Place Name Data Structures ======================= //
229 // ============== Place Name Data Structures ======================= //
231 private static class PlaceNameChunk implements Cacheable
233 final PlaceNameService placeNameService;
234 final StringBuilder textArray;
235 final int[] textIndexArray;
236 final double[] latlonArray;
237 final int numEntries;
238 final long estimatedMemorySize;
240 PlaceNameChunk(PlaceNameService service, StringBuilder text, int[] textIndices,
241 double[] positions, int numEntries)
243 this.placeNameService = service;
244 this.textArray = text;
245 this.textIndexArray = textIndices;
246 this.latlonArray = positions;
247 this.numEntries = numEntries;
248 this.estimatedMemorySize = this.computeEstimatedMemorySize();
251 long computeEstimatedMemorySize()
253 long result = 0;
254 result += BufferUtil.SIZEOF_SHORT * textArray.capacity();
255 result += BufferUtil.SIZEOF_INT * textIndexArray.length;
256 result += BufferUtil.SIZEOF_DOUBLE * latlonArray.length;
257 return result;
260 Position getPosition(int index)
262 int latlonIndex = 2 * index;
263 return Position.fromDegrees(latlonArray[latlonIndex], latlonArray[latlonIndex + 1], 0);
266 PlaceNameService getPlaceNameService()
268 return this.placeNameService;
271 String getText(int index)
273 int beginIndex = textIndexArray[index];
274 int endIndex = (index + 1 < numEntries) ? textIndexArray[index + 1] : textArray.length();
275 return this.textArray.substring(beginIndex, endIndex);
278 public long getSizeInBytes()
280 return this.estimatedMemorySize;
283 Iterator<PlaceName> createRenderIterator(final DrawContext dc)
285 return new Iterator<PlaceName>()
287 PlaceNameImpl placeNameProxy = new PlaceNameImpl();
288 int index = -1;
290 public boolean hasNext()
292 return index < (PlaceNameChunk.this.numEntries - 1);
295 public PlaceName next()
297 if (!hasNext())
298 throw new NoSuchElementException();
299 this.updateProxy(placeNameProxy, ++index);
300 return placeNameProxy;
303 public void remove()
305 throw new UnsupportedOperationException();
308 void updateProxy(PlaceNameImpl proxy, int index)
310 proxy.text = getText(index);
311 proxy.position = getPosition(index);
312 proxy.font = placeNameService.getFont();
313 proxy.color = placeNameService.getColor();
314 proxy.visible = isNameVisible(dc, placeNameService, proxy.position);
320 private static class PlaceNameImpl implements PlaceName
322 String text;
323 Position position;
324 Font font;
325 Color color;
326 boolean visible;
328 PlaceNameImpl()
332 public String getText()
334 return this.text;
337 public void setText(String text)
339 throw new UnsupportedOperationException();
342 public Position getPosition()
344 return this.position;
347 public void setPosition(Position position)
349 throw new UnsupportedOperationException();
352 public Font getFont()
354 return this.font;
357 public void setFont(Font font)
359 throw new UnsupportedOperationException();
362 public Color getColor()
364 return this.color;
367 public void setColor(Color color)
369 throw new UnsupportedOperationException();
372 public boolean isVisible()
374 return this.visible;
377 public void setVisible(boolean visible)
379 throw new UnsupportedOperationException();
382 public WWIcon getIcon()
384 return null;
387 public void setIcon(WWIcon icon)
389 throw new UnsupportedOperationException();
393 // ============== Rendering ======================= //
394 // ============== Rendering ======================= //
395 // ============== Rendering ======================= //
397 private final PlaceNameRenderer placeNameRenderer = new PlaceNameRenderer();
399 @Override
400 protected void doRender(DrawContext dc)
402 final boolean enableDepthTest = dc.getView().getAltitude()
403 < (dc.getVerticalExaggeration() * dc.getGlobe().getMaxElevation());
405 int serviceCount = this.placeNameServiceSet.getServiceCount();
406 for (int i = 0; i < serviceCount; i++)
408 PlaceNameService placeNameService = this.placeNameServiceSet.getService(i);
409 if (!isServiceVisible(dc, placeNameService))
410 continue;
412 double minDist = placeNameService.getMinDisplayDistance();
413 double maxDist = placeNameService.getMaxDisplayDistance();
414 double minDistSquared = minDist * minDist;
415 double maxDistSquared = maxDist * maxDist;
417 Tile[] tiles = this.tiles.get(i);
418 for (Tile tile : tiles)
422 drawOrRequestTile(dc, tile, minDistSquared, maxDistSquared, enableDepthTest);
424 catch (Exception e)
426 String message = WorldWind.retrieveErrMsg("layers.PlaceNameLayer.ExceptionRenderingTile");
427 WorldWind.logger().log(Level.FINE, message, e);
432 this.sendRequests();
435 private void drawOrRequestTile(DrawContext dc, Tile tile, double minDisplayDistanceSquared,
436 double maxDisplayDistanceSquared, boolean enableDepthTest)
438 if (!isTileVisible(dc, tile, minDisplayDistanceSquared, maxDisplayDistanceSquared))
439 return;
441 Object cacheObj = WorldWind.memoryCache().getObject(tile);
442 if (cacheObj == null)
444 this.requestTile(this.readQueue, tile);
445 return;
448 if (!(cacheObj instanceof PlaceNameChunk))
449 return;
451 PlaceNameChunk placeNameChunk = (PlaceNameChunk) cacheObj;
452 Iterator<PlaceName> renderIter = placeNameChunk.createRenderIterator(dc);
453 placeNameRenderer.render(dc, renderIter, enableDepthTest);
456 private static boolean isServiceVisible(DrawContext dc, PlaceNameService placeNameService)
458 if (!placeNameService.isEnabled())
459 return false;
460 //noinspection SimplifiableIfStatement
461 if (dc.getVisibleSector() != null && !placeNameService.getSector().intersects(dc.getVisibleSector()))
462 return false;
464 return placeNameService.getExtent(dc).intersects(dc.getView().getFrustumInModelCoordinates());
467 private static boolean isTileVisible(DrawContext dc, Tile tile, double minDistanceSquared,
468 double maxDistanceSquared)
470 if (!tile.getSector().intersects(dc.getVisibleSector()))
471 return false;
473 View view = dc.getView();
474 Angle lat = clampAngle(view.getLatitude(), tile.getSector().getMinLatitude(),
475 tile.getSector().getMaxLatitude());
476 Angle lon = clampAngle(view.getLongitude(), tile.getSector().getMinLongitude(),
477 tile.getSector().getMaxLongitude());
478 Vec4 p = dc.getGlobe().computePointFromPosition(lat, lon, 0d);
479 double distSquared = dc.getView().getEyePoint().distanceToSquared3(p);
480 //noinspection RedundantIfStatement
481 if (minDistanceSquared > distSquared || maxDistanceSquared < distSquared)
482 return false;
484 return true;
487 private static boolean isNameVisible(DrawContext dc, PlaceNameService service, Position namePosition)
489 double elevation = dc.getVerticalExaggeration() * namePosition.getElevation();
490 Vec4 namePoint = dc.getGlobe().computePointFromPosition(namePosition.getLatitude(),
491 namePosition.getLongitude(), elevation);
492 Vec4 eyeVec = dc.getView().getEyePoint();
494 double dist = eyeVec.distanceTo3(namePoint);
495 return dist >= service.getMinDisplayDistance() && dist <= service.getMaxDisplayDistance();
498 private static Angle clampAngle(Angle a, Angle min, Angle max)
500 return a.compareTo(min) < 0 ? min : (a.compareTo(max) > 0 ? max : a);
503 // ============== Image Reading and Downloading ======================= //
504 // ============== Image Reading and Downloading ======================= //
505 // ============== Image Reading and Downloading ======================= //
507 private static final int MAX_REQUESTS = 64;
508 private final Queue<Tile> downloadQueue = new LinkedBlockingQueue<Tile>(MAX_REQUESTS);
509 private final Queue<Tile> readQueue = new LinkedBlockingQueue<Tile>(MAX_REQUESTS);
511 private static class RequestTask implements Runnable
513 final PlaceNameLayer layer;
514 final Tile tile;
516 RequestTask(PlaceNameLayer layer, Tile tile)
518 this.layer = layer;
519 this.tile = tile;
522 @Override
523 public boolean equals(Object o)
525 if (this == o)
526 return true;
527 if (o == null || this.getClass() != o.getClass())
528 return false;
530 final RequestTask other = (RequestTask) o;
532 // Don't include layer in comparison so that requests are shared among layers
533 return !(this.tile != null ? !this.tile.equals(other.tile) : other.tile != null);
536 @Override
537 public int hashCode()
539 return (this.tile != null ? this.tile.hashCode() : 0);
542 public void run()
544 synchronized (tile)
546 if (WorldWind.memoryCache().getObject(this.tile) != null)
547 return;
549 final java.net.URL tileURL = WorldWind.dataFileCache().findFile(tile.getFileCachePath(), false);
550 if (tileURL != null)
552 if (this.layer.loadTile(this.tile, tileURL))
554 tile.getPlaceNameService().unmarkResourceAbsent(tile.getPlaceNameService().getTileNumber(
555 tile.row,
556 tile.column));
557 this.layer.firePropertyChange(AVKey.LAYER, null, this);
559 else
561 // Assume that something's wrong with the file and delete it.
562 WorldWind.dataFileCache().removeFile(tileURL);
563 tile.getPlaceNameService().markResourceAbsent(tile.getPlaceNameService().getTileNumber(tile.row,
564 tile.column));
565 String message = WorldWind.retrieveErrMsg("generic.DeletedCorruptDataFile") + tileURL;
566 WorldWind.logger().log(Level.FINE, message);
568 return;
572 this.layer.requestTile(this.layer.downloadQueue, this.tile);
575 public String toString()
577 return this.tile.toString();
581 private static class DownloadPostProcessor implements RetrievalPostProcessor
583 final PlaceNameLayer layer;
584 final Tile tile;
586 private DownloadPostProcessor(PlaceNameLayer layer, Tile tile)
588 this.layer = layer;
589 this.tile = tile;
592 public java.nio.ByteBuffer run(Retriever retriever)
594 if (retriever == null)
596 String msg = WorldWind.retrieveErrMsg("nullValue.RetrieverIsNull");
597 WorldWind.logger().log(Level.FINE, msg);
598 throw new IllegalArgumentException(msg);
601 if (!retriever.getState().equals(Retriever.RETRIEVER_STATE_SUCCESSFUL))
602 return null;
604 if (!(retriever instanceof URLRetriever))
605 return null;
609 if (retriever instanceof HTTPRetriever)
611 HTTPRetriever httpRetriever = (HTTPRetriever) retriever;
612 if (httpRetriever.getResponseCode() == java.net.HttpURLConnection.HTTP_NO_CONTENT)
614 // Mark tile as missing to avoid further attempts
615 tile.getPlaceNameService().markResourceAbsent(tile.getPlaceNameService().getTileNumber(tile.row,
616 tile.column));
617 return null;
621 URLRetriever urlRetriever = (URLRetriever) retriever;
622 java.nio.ByteBuffer buffer = urlRetriever.getBuffer();
624 synchronized (tile)
626 final java.io.File cacheFile = WorldWind.dataFileCache().newFile(this.tile.getFileCachePath());
627 if (cacheFile == null)
629 String msg = WorldWind.retrieveErrMsg("generic.CantCreateCacheFile")
630 + this.tile.getFileCachePath();
631 WorldWind.logger().log(Level.FINE, msg);
632 return null;
635 if (cacheFile.exists())
636 return buffer; // info is already here; don't need to do anything
638 if (buffer != null)
640 WWIO.saveBuffer(buffer, cacheFile);
641 this.layer.firePropertyChange(AVKey.LAYER, null, this);
642 return buffer;
646 catch (java.io.IOException e)
648 String message = WorldWind.retrieveErrMsg("layers.PlaceNameLayer.ExceptionSavingRetrievedFile");
649 WorldWind.logger().log(Level.FINE, message + this.tile.getFileCachePath(), e);
652 return null;
656 private static class GMLPlaceNameSAXHandler extends org.xml.sax.helpers.DefaultHandler
658 static final String GML_FEATURE_MEMBER = "gml:featureMember";
659 static final String TOPP_FULL_NAME_ND = "topp:full_name_nd";
660 static final String TOPP_LATITUDE = "topp:latitude";
661 static final String TOPP_LONGITUDE = "topp:longitude";
662 final LinkedList<String> qNameStack = new LinkedList<String>();
663 boolean inBeginEndPair = false;
664 StringBuilder latBuffer = new StringBuilder();
665 StringBuilder lonBuffer = new StringBuilder();
667 StringBuilder textArray = new StringBuilder();
668 int[] textIndexArray = new int[16];
669 double[] latlonArray = new double[16];
670 int numEntries = 0;
672 GMLPlaceNameSAXHandler()
676 PlaceNameChunk createPlaceNameChunk(PlaceNameService service)
678 return new PlaceNameChunk(service, this.textArray, this.textIndexArray, this.latlonArray, this.numEntries);
681 void beginEntry()
683 int textIndex = this.textArray.length();
684 this.textIndexArray = append(this.textIndexArray, this.numEntries, textIndex);
685 this.inBeginEndPair = true;
688 void endEntry()
690 double lat = this.parseDouble(this.latBuffer);
691 double lon = this.parseDouble(this.lonBuffer);
692 int numLatLon = 2 * this.numEntries;
693 this.latlonArray = this.append(this.latlonArray, numLatLon, lat);
694 numLatLon++;
695 this.latlonArray = this.append(this.latlonArray, numLatLon, lon);
697 this.latBuffer.delete(0, this.latBuffer.length());
698 this.lonBuffer.delete(0, this.lonBuffer.length());
699 this.inBeginEndPair = false;
700 this.numEntries++;
703 double parseDouble(StringBuilder sb)
705 double value = 0;
708 value = Double.parseDouble(sb.toString());
710 catch (NumberFormatException e)
712 String msg = WorldWind.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
713 WorldWind.logger().log(Level.FINE, msg, e);
715 return value;
718 int[] append(int[] array, int index, int value)
720 if (index >= array.length)
721 array = this.resizeArray(array);
722 array[index] = value;
723 return array;
726 int[] resizeArray(int[] oldArray)
728 int newSize = 2 * oldArray.length;
729 int[] newArray = new int[newSize];
730 System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
731 return newArray;
734 double[] append(double[] array, int index, double value)
736 if (index >= array.length)
737 array = this.resizeArray(array);
738 array[index] = value;
739 return array;
742 double[] resizeArray(double[] oldArray)
744 int newSize = 2 * oldArray.length;
745 double[] newArray = new double[newSize];
746 System.arraycopy(oldArray, 0, newArray, 0, oldArray.length);
747 return newArray;
750 public void characters(char ch[], int start, int length)
752 if (!this.inBeginEndPair)
753 return;
755 String top = this.qNameStack.getFirst();
757 StringBuilder sb = null;
758 if (TOPP_LATITUDE == top)
759 sb = this.latBuffer;
760 else if (TOPP_LONGITUDE == top)
761 sb = this.lonBuffer;
762 else if (TOPP_FULL_NAME_ND == top)
763 sb = this.textArray;
765 if (sb != null)
766 sb.append(ch, start, length);
769 public void startElement(String uri, String localName, String qName, org.xml.sax.Attributes attributes)
771 String internQName = qName.intern();
772 // Don't validate uri, localName or attributes because they aren't used.
773 if (GML_FEATURE_MEMBER == internQName)
774 this.beginEntry();
775 this.qNameStack.addFirst(qName);
778 public void endElement(String uri, String localName, String qName)
780 String internQName = qName.intern();
781 // Don't validate uri or localName because they aren't used.
782 if (GML_FEATURE_MEMBER == internQName)
783 this.endEntry();
784 this.qNameStack.removeFirst();
788 private boolean loadTile(Tile tile, java.net.URL url)
790 PlaceNameChunk placeNameChunk = readTile(tile, url);
791 if (placeNameChunk == null)
792 return false;
794 WorldWind.memoryCache().add(tile, placeNameChunk);
795 return true;
798 private static PlaceNameChunk readTile(Tile tile, java.net.URL url)
800 java.io.InputStream is = null;
804 String path = url.getFile();
805 path = path.replaceAll("%20", " "); // TODO: find a better way to get a path usable by FileInputStream
807 java.io.FileInputStream fis = new java.io.FileInputStream(path);
808 java.io.BufferedInputStream buf = new java.io.BufferedInputStream(fis);
809 is = new java.util.zip.GZIPInputStream(buf);
811 GMLPlaceNameSAXHandler handler = new GMLPlaceNameSAXHandler();
812 javax.xml.parsers.SAXParserFactory.newInstance().newSAXParser().parse(is, handler);
813 return handler.createPlaceNameChunk(tile.getPlaceNameService());
815 catch (Exception e)
817 String message = WorldWind.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
818 WorldWind.logger().log(Level.FINE, message, e);
820 finally
824 if (is != null)
825 is.close();
827 catch (java.io.IOException e)
829 String message = WorldWind.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
830 WorldWind.logger().log(Level.FINE, message, e);
834 return null;
837 private void requestTile(Queue<Tile> queue, Tile tile)
839 if (tile.getPlaceNameService().isResourceAbsent(tile.getPlaceNameService().getTileNumber(
840 tile.row, tile.column)))
841 return;
842 if (!queue.contains(tile))
843 queue.offer(tile);
846 private void sendRequests()
848 Tile tile;
849 // Send threaded read tasks.
850 while (!WorldWind.threadedTaskService().isFull() && (tile = this.readQueue.poll()) != null)
852 WorldWind.threadedTaskService().addTask(new RequestTask(this, tile));
854 // Send retriever tasks.
855 while (!WorldWind.retrievalService().isFull() && (tile = this.downloadQueue.poll()) != null)
857 java.net.URL url;
860 url = tile.getRequestURL();
862 catch (java.net.MalformedURLException e)
864 String message = WorldWind.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToDownloadFile");
865 WorldWind.logger().log(Level.FINE, message + tile, e);
866 return;
868 WorldWind.retrievalService().runRetriever(new HTTPRetriever(url, new DownloadPostProcessor(this, tile)));