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 gov
.nasa
.worldwind
.*;
10 import gov
.nasa
.worldwind
.geom
.*;
14 import java
.util
.List
;
15 import java
.util
.Queue
;
16 import java
.util
.concurrent
.*;
17 import java
.util
.logging
.Level
;
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
[]>();
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
;
65 String fileCachePath
= 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
)
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
)
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
;
118 this.column
= column
;
119 this.hash
= this.computeHash();
124 return this.getFileCachePath() != null ?
this.getFileCachePath().hashCode() : 0;
128 public boolean equals(Object o
)
132 if (o
== null || this.getClass() != o
.getClass())
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
)
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
,
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
);
183 public int hashCode()
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
++)
212 Angle t1
= Tile
.computeColumnLongitude(firstCol
, dLon
);
213 for (int col
= firstCol
; col
<= lastCol
; col
++)
218 tiles
[col
+ row
* nLonTiles
] = new Tile(placeNameService
, new Sector(p1
, p2
, t1
, t2
), row
, col
);
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()
254 result
+= BufferUtil
.SIZEOF_SHORT
* textArray
.capacity();
255 result
+= BufferUtil
.SIZEOF_INT
* textIndexArray
.length
;
256 result
+= BufferUtil
.SIZEOF_DOUBLE
* latlonArray
.length
;
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();
290 public boolean hasNext()
292 return index
< (PlaceNameChunk
.this.numEntries
- 1);
295 public PlaceName
next()
298 throw new NoSuchElementException();
299 this.updateProxy(placeNameProxy
, ++index
);
300 return placeNameProxy
;
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
332 public String
getText()
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()
357 public void setFont(Font font
)
359 throw new UnsupportedOperationException();
362 public Color
getColor()
367 public void setColor(Color color
)
369 throw new UnsupportedOperationException();
372 public boolean isVisible()
377 public void setVisible(boolean visible
)
379 throw new UnsupportedOperationException();
382 public WWIcon
getIcon()
387 public void setIcon(WWIcon icon
)
389 throw new UnsupportedOperationException();
393 // ============== Rendering ======================= //
394 // ============== Rendering ======================= //
395 // ============== Rendering ======================= //
397 private final PlaceNameRenderer placeNameRenderer
= new PlaceNameRenderer();
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
))
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
);
426 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionRenderingTile");
427 WorldWind
.logger().log(Level
.FINE
, message
, e
);
435 private void drawOrRequestTile(DrawContext dc
, Tile tile
, double minDisplayDistanceSquared
,
436 double maxDisplayDistanceSquared
, boolean enableDepthTest
)
438 if (!isTileVisible(dc
, tile
, minDisplayDistanceSquared
, maxDisplayDistanceSquared
))
441 Object cacheObj
= WorldWind
.memoryCache().getObject(tile
);
442 if (cacheObj
== null)
444 this.requestTile(this.readQueue
, tile
);
448 if (!(cacheObj
instanceof PlaceNameChunk
))
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())
460 //noinspection SimplifiableIfStatement
461 if (dc
.getVisibleSector() != null && !placeNameService
.getSector().intersects(dc
.getVisibleSector()))
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()))
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
)
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
;
516 RequestTask(PlaceNameLayer layer
, Tile tile
)
523 public boolean equals(Object o
)
527 if (o
== null || this.getClass() != o
.getClass())
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);
537 public int hashCode()
539 return (this.tile
!= null ?
this.tile
.hashCode() : 0);
546 if (WorldWind
.memoryCache().getObject(this.tile
) != null)
549 final java
.net
.URL tileURL
= WorldWind
.dataFileCache().findFile(tile
.getFileCachePath(), false);
552 if (this.layer
.loadTile(this.tile
, tileURL
))
554 tile
.getPlaceNameService().unmarkResourceAbsent(tile
.getPlaceNameService().getTileNumber(
557 this.layer
.firePropertyChange(AVKey
.LAYER
, null, this);
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
,
565 String message
= WorldWind
.retrieveErrMsg("generic.DeletedCorruptDataFile") + tileURL
;
566 WorldWind
.logger().log(Level
.FINE
, message
);
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
;
586 private DownloadPostProcessor(PlaceNameLayer layer
, 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
))
604 if (!(retriever
instanceof URLRetriever
))
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
,
621 URLRetriever urlRetriever
= (URLRetriever
) retriever
;
622 java
.nio
.ByteBuffer buffer
= urlRetriever
.getBuffer();
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
);
635 if (cacheFile
.exists())
636 return buffer
; // info is already here; don't need to do anything
640 WWIO
.saveBuffer(buffer
, cacheFile
);
641 this.layer
.firePropertyChange(AVKey
.LAYER
, null, this);
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
);
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];
672 GMLPlaceNameSAXHandler()
676 PlaceNameChunk
createPlaceNameChunk(PlaceNameService service
)
678 return new PlaceNameChunk(service
, this.textArray
, this.textIndexArray
, this.latlonArray
, this.numEntries
);
683 int textIndex
= this.textArray
.length();
684 this.textIndexArray
= append(this.textIndexArray
, this.numEntries
, textIndex
);
685 this.inBeginEndPair
= true;
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
);
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;
703 double parseDouble(StringBuilder sb
)
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
);
718 int[] append(int[] array
, int index
, int value
)
720 if (index
>= array
.length
)
721 array
= this.resizeArray(array
);
722 array
[index
] = value
;
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
);
734 double[] append(double[] array
, int index
, double value
)
736 if (index
>= array
.length
)
737 array
= this.resizeArray(array
);
738 array
[index
] = value
;
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
);
750 public void characters(char ch
[], int start
, int length
)
752 if (!this.inBeginEndPair
)
755 String top
= this.qNameStack
.getFirst();
757 StringBuilder sb
= null;
758 if (TOPP_LATITUDE
== top
)
760 else if (TOPP_LONGITUDE
== top
)
762 else if (TOPP_FULL_NAME_ND
== top
)
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
)
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
)
784 this.qNameStack
.removeFirst();
788 private boolean loadTile(Tile tile
, java
.net
.URL url
)
790 PlaceNameChunk placeNameChunk
= readTile(tile
, url
);
791 if (placeNameChunk
== null)
794 WorldWind
.memoryCache().add(tile
, placeNameChunk
);
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());
817 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
818 WorldWind
.logger().log(Level
.FINE
, message
, e
);
827 catch (java
.io
.IOException e
)
829 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
830 WorldWind
.logger().log(Level
.FINE
, message
, e
);
837 private void requestTile(Queue
<Tile
> queue
, Tile tile
)
839 if (tile
.getPlaceNameService().isResourceAbsent(tile
.getPlaceNameService().getTileNumber(
840 tile
.row
, tile
.column
)))
842 if (!queue
.contains(tile
))
846 private void sendRequests()
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)
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
);
868 WorldWind
.retrievalService().runRetriever(new HTTPRetriever(url
, new DownloadPostProcessor(this, tile
)));