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
.*;
11 import gov
.nasa
.worldwind
.geom
.Point
;
15 import java
.util
.List
;
16 import java
.util
.Queue
;
17 import java
.util
.concurrent
.*;
18 import java
.util
.logging
.Level
;
21 * @author Paul Collins
22 * @version $Id: PlaceNameLayer.java 1788 2007-05-08 17:12:05Z dcollins $
24 public class PlaceNameLayer
extends AbstractLayer
26 private final PlaceNameServiceSet placeNameServiceSet
;
27 private final List
<Tile
[]> tiles
= new ArrayList
<Tile
[]>();
30 * @param placeNameServiceSet the set of PlaceNameService objects that PlaceNameLayer will render.
31 * @throws IllegalArgumentException if <code>placeNameServiceSet</code> is null
33 public PlaceNameLayer(PlaceNameServiceSet placeNameServiceSet
)
35 if (placeNameServiceSet
== null)
37 String message
= WorldWind
.retrieveErrMsg("nullValue.PlaceNameServiceSetIsNull");
38 WorldWind
.logger().log(Level
.FINE
, message
);
39 throw new IllegalArgumentException(message
);
42 this.placeNameServiceSet
= placeNameServiceSet
.deepCopy();
43 for (int i
= 0; i
< this.placeNameServiceSet
.getServiceCount(); i
++)
45 tiles
.add(i
, buildTiles(this.placeNameServiceSet
.getService(i
)));
49 public final PlaceNameServiceSet
getPlaceNameServiceSet()
51 return this.placeNameServiceSet
;
54 // ============== Tile Assembly ======================= //
55 // ============== Tile Assembly ======================= //
56 // ============== Tile Assembly ======================= //
58 private static class Tile
60 final PlaceNameService placeNameService
;
66 String fileCachePath
= null;
68 double extentVerticalExaggeration
= Double
.MIN_VALUE
;
70 static int computeRow(Angle delta
, Angle latitude
)
72 if (delta
== null || latitude
== null)
74 String msg
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
75 WorldWind
.logger().log(Level
.FINE
, msg
);
76 throw new IllegalArgumentException(msg
);
78 return (int) ((latitude
.getDegrees() + 90d
) / delta
.getDegrees());
81 static int computeColumn(Angle delta
, Angle longitude
)
83 if (delta
== null || longitude
== null)
85 String msg
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
86 WorldWind
.logger().log(Level
.FINE
, msg
);
87 throw new IllegalArgumentException(msg
);
89 return (int) ((longitude
.getDegrees() + 180d
) / delta
.getDegrees());
92 static Angle
computeRowLatitude(int row
, Angle delta
)
96 String msg
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
97 WorldWind
.logger().log(Level
.FINE
, msg
);
98 throw new IllegalArgumentException(msg
);
100 return Angle
.fromDegrees(-90d
+ delta
.getDegrees() * row
);
103 static Angle
computeColumnLongitude(int column
, Angle delta
)
107 String msg
= WorldWind
.retrieveErrMsg("nullValue.AngleIsNull");
108 WorldWind
.logger().log(Level
.FINE
, msg
);
109 throw new IllegalArgumentException(msg
);
111 return Angle
.fromDegrees(-180 + delta
.getDegrees() * column
);
114 Tile(PlaceNameService placeNameService
, Sector sector
, int row
, int column
)
116 this.placeNameService
= placeNameService
;
117 this.sector
= sector
;
119 this.column
= column
;
120 this.hash
= this.computeHash();
125 return this.getFileCachePath() != null ?
this.getFileCachePath().hashCode() : 0;
129 public boolean equals(Object o
)
133 if (o
== null || this.getClass() != o
.getClass())
136 final Tile other
= (Tile
) o
;
138 return this.getFileCachePath() != null ?
!this.getFileCachePath().equals(other
.getFileCachePath()) :
139 other
.getFileCachePath() != null;
142 Extent
getExtent(DrawContext dc
)
146 String message
= WorldWind
.retrieveErrMsg("nullValue.DrawContextIsNull");
147 WorldWind
.logger().log(Level
.FINE
, message
);
148 throw new IllegalArgumentException(message
);
151 if (this.extent
== null || this.extentVerticalExaggeration
!= dc
.getVerticalExaggeration())
153 this.extentVerticalExaggeration
= dc
.getVerticalExaggeration();
154 this.extent
= Sector
.computeBoundingCylinder(dc
.getGlobe(), this.extentVerticalExaggeration
,
161 String
getFileCachePath()
163 if (this.fileCachePath
== null)
164 this.fileCachePath
= this.placeNameService
.createFileCachePathFromTile(this.row
, this.column
);
166 return this.fileCachePath
;
169 PlaceNameService
getPlaceNameService()
171 return placeNameService
;
174 java
.net
.URL
getRequestURL() throws java
.net
.MalformedURLException
176 return this.placeNameService
.createServiceURLFromSector(this.sector
);
184 public int hashCode()
190 private Tile
[] buildTiles(PlaceNameService placeNameService
)
192 final Sector sector
= placeNameService
.getSector();
193 final Angle dLat
= placeNameService
.getTileDelta().getLatitude();
194 final Angle dLon
= placeNameService
.getTileDelta().getLongitude();
196 // Determine the row and column offset from the global tiling origin for the southwest tile corner
197 int firstRow
= Tile
.computeRow(dLat
, sector
.getMinLatitude());
198 int firstCol
= Tile
.computeColumn(dLon
, sector
.getMinLongitude());
199 int lastRow
= Tile
.computeRow(dLat
, sector
.getMaxLatitude().subtract(dLat
));
200 int lastCol
= Tile
.computeColumn(dLon
, sector
.getMaxLongitude().subtract(dLon
));
202 int nLatTiles
= lastRow
- firstRow
+ 1;
203 int nLonTiles
= lastCol
- firstCol
+ 1;
205 Tile
[] tiles
= new Tile
[nLatTiles
* nLonTiles
];
207 Angle p1
= Tile
.computeRowLatitude(firstRow
, dLat
);
208 for (int row
= firstRow
; row
<= lastRow
; row
++)
213 Angle t1
= Tile
.computeColumnLongitude(firstCol
, dLon
);
214 for (int col
= firstCol
; col
<= lastCol
; col
++)
219 tiles
[col
+ row
* nLonTiles
] = new Tile(placeNameService
, new Sector(p1
, p2
, t1
, t2
), row
, col
);
228 // ============== Place Name Data Structures ======================= //
229 // ============== Place Name Data Structures ======================= //
230 // ============== Place Name Data Structures ======================= //
232 private static class PlaceNameChunk
implements Cacheable
234 final PlaceNameService placeNameService
;
235 final StringBuilder textArray
;
236 final int[] textIndexArray
;
237 final double[] latlonArray
;
238 final int numEntries
;
239 final long estimatedMemorySize
;
241 PlaceNameChunk(PlaceNameService service
, StringBuilder text
, int[] textIndices
,
242 double[] positions
, int numEntries
)
244 this.placeNameService
= service
;
245 this.textArray
= text
;
246 this.textIndexArray
= textIndices
;
247 this.latlonArray
= positions
;
248 this.numEntries
= numEntries
;
249 this.estimatedMemorySize
= this.computeEstimatedMemorySize();
252 long computeEstimatedMemorySize()
255 result
+= BufferUtil
.SIZEOF_SHORT
* textArray
.capacity();
256 result
+= BufferUtil
.SIZEOF_INT
* textIndexArray
.length
;
257 result
+= BufferUtil
.SIZEOF_DOUBLE
* latlonArray
.length
;
261 Position
getPosition(int index
)
263 int latlonIndex
= 2 * index
;
264 return Position
.fromDegrees(latlonArray
[latlonIndex
], latlonArray
[latlonIndex
+ 1], 0);
267 PlaceNameService
getPlaceNameService()
269 return this.placeNameService
;
272 String
getText(int index
)
274 int beginIndex
= textIndexArray
[index
];
275 int endIndex
= (index
+ 1 < numEntries
) ? textIndexArray
[index
+ 1] : textArray
.length();
276 return this.textArray
.substring(beginIndex
, endIndex
);
279 public long getSizeInBytes()
281 return this.estimatedMemorySize
;
284 Iterator
<PlaceName
> createRenderIterator(final DrawContext dc
)
286 return new Iterator
<PlaceName
>()
288 PlaceNameImpl placeNameProxy
= new PlaceNameImpl();
291 public boolean hasNext()
293 return index
< (PlaceNameChunk
.this.numEntries
- 1);
296 public PlaceName
next()
299 throw new NoSuchElementException();
300 this.updateProxy(placeNameProxy
, ++index
);
301 return placeNameProxy
;
306 throw new UnsupportedOperationException();
309 void updateProxy(PlaceNameImpl proxy
, int index
)
311 proxy
.text
= getText(index
);
312 proxy
.position
= getPosition(index
);
313 proxy
.font
= placeNameService
.getFont();
314 proxy
.color
= placeNameService
.getColor();
315 proxy
.visible
= isNameVisible(dc
, placeNameService
, proxy
.position
);
321 private static class PlaceNameImpl
implements PlaceName
333 public String
getText()
338 public void setText(String text
)
340 throw new UnsupportedOperationException();
343 public Position
getPosition()
345 return this.position
;
348 public void setPosition(Position position
)
350 throw new UnsupportedOperationException();
353 public Font
getFont()
358 public void setFont(Font font
)
360 throw new UnsupportedOperationException();
363 public Color
getColor()
368 public void setColor(Color color
)
370 throw new UnsupportedOperationException();
373 public boolean isVisible()
378 public void setVisible(boolean visible
)
380 throw new UnsupportedOperationException();
383 public WWIcon
getIcon()
388 public void setIcon(WWIcon icon
)
390 throw new UnsupportedOperationException();
394 // ============== Rendering ======================= //
395 // ============== Rendering ======================= //
396 // ============== Rendering ======================= //
398 private final PlaceNameRenderer placeNameRenderer
= new PlaceNameRenderer();
401 protected void doRender(DrawContext dc
)
403 final boolean enableDepthTest
= dc
.getView().getAltitude()
404 < (dc
.getVerticalExaggeration() * dc
.getGlobe().getMaxElevation());
406 int serviceCount
= this.placeNameServiceSet
.getServiceCount();
407 for (int i
= 0; i
< serviceCount
; i
++)
409 PlaceNameService placeNameService
= this.placeNameServiceSet
.getService(i
);
410 if (!isServiceVisible(dc
, placeNameService
))
413 double minDist
= placeNameService
.getMinDisplayDistance();
414 double maxDist
= placeNameService
.getMaxDisplayDistance();
415 double minDistSquared
= minDist
* minDist
;
416 double maxDistSquared
= maxDist
* maxDist
;
418 Tile
[] tiles
= this.tiles
.get(i
);
419 for (Tile tile
: tiles
)
423 drawOrRequestTile(dc
, tile
, minDistSquared
, maxDistSquared
, enableDepthTest
);
427 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionRenderingTile");
428 WorldWind
.logger().log(Level
.FINE
, message
, e
);
436 private void drawOrRequestTile(DrawContext dc
, Tile tile
, double minDisplayDistanceSquared
,
437 double maxDisplayDistanceSquared
, boolean enableDepthTest
)
439 if (!isTileVisible(dc
, tile
, minDisplayDistanceSquared
, maxDisplayDistanceSquared
))
442 Object cacheObj
= WorldWind
.memoryCache().getObject(tile
);
443 if (cacheObj
== null)
445 this.requestTile(this.readQueue
, tile
);
449 if (!(cacheObj
instanceof PlaceNameChunk
))
452 PlaceNameChunk placeNameChunk
= (PlaceNameChunk
) cacheObj
;
453 Iterator
<PlaceName
> renderIter
= placeNameChunk
.createRenderIterator(dc
);
454 placeNameRenderer
.render(dc
, renderIter
, enableDepthTest
);
457 private static boolean isServiceVisible(DrawContext dc
, PlaceNameService placeNameService
)
459 if (!placeNameService
.isEnabled())
461 //noinspection SimplifiableIfStatement
462 if (dc
.getVisibleSector() != null && !placeNameService
.getSector().intersects(dc
.getVisibleSector()))
465 return placeNameService
.getExtent(dc
).intersects(dc
.getView().getFrustumInModelCoordinates());
468 private static boolean isTileVisible(DrawContext dc
, Tile tile
, double minDistanceSquared
,
469 double maxDistanceSquared
)
471 if (!tile
.getSector().intersects(dc
.getVisibleSector()))
474 Position viewPosition
= dc
.getView().getPosition();
475 Angle lat
= clampAngle(viewPosition
.getLatitude(), tile
.getSector().getMinLatitude(),
476 tile
.getSector().getMaxLatitude());
477 Angle lon
= clampAngle(viewPosition
.getLongitude(), tile
.getSector().getMinLongitude(),
478 tile
.getSector().getMaxLongitude());
479 Point p
= dc
.getGlobe().computePointFromPosition(lat
, lon
, 0d
);
480 double distSquared
= dc
.getView().getEyePoint().distanceToSquared(p
);
481 //noinspection RedundantIfStatement
482 if (minDistanceSquared
> distSquared
|| maxDistanceSquared
< distSquared
)
488 private static boolean isNameVisible(DrawContext dc
, PlaceNameService service
, Position namePosition
)
490 double elevation
= dc
.getVerticalExaggeration() * namePosition
.getElevation();
491 Point namePoint
= dc
.getGlobe().computePointFromPosition(namePosition
.getLatitude(),
492 namePosition
.getLongitude(), elevation
);
493 Point eye
= dc
.getView().getEyePoint();
495 double dist
= eye
.distanceTo(namePoint
);
496 return dist
>= service
.getMinDisplayDistance() && dist
<= service
.getMaxDisplayDistance();
499 private static Angle
clampAngle(Angle a
, Angle min
, Angle max
)
501 return a
.compareTo(min
) < 0 ? min
: (a
.compareTo(max
) > 0 ? max
: a
);
504 // ============== Image Reading and Downloading ======================= //
505 // ============== Image Reading and Downloading ======================= //
506 // ============== Image Reading and Downloading ======================= //
508 private static final int MAX_REQUESTS
= 64;
509 private final Queue
<Tile
> downloadQueue
= new LinkedBlockingQueue
<Tile
>(MAX_REQUESTS
);
510 private final Queue
<Tile
> readQueue
= new LinkedBlockingQueue
<Tile
>(MAX_REQUESTS
);
512 private static class RequestTask
implements Runnable
514 final PlaceNameLayer layer
;
517 RequestTask(PlaceNameLayer layer
, Tile tile
)
524 public boolean equals(Object o
)
528 if (o
== null || this.getClass() != o
.getClass())
531 final RequestTask other
= (RequestTask
) o
;
533 // Don't include layer in comparison so that requests are shared among layers
534 return !(this.tile
!= null ?
!this.tile
.equals(other
.tile
) : other
.tile
!= null);
538 public int hashCode()
540 return (this.tile
!= null ?
this.tile
.hashCode() : 0);
547 if (WorldWind
.memoryCache().getObject(this.tile
) != null)
550 final java
.net
.URL tileURL
= WorldWind
.dataFileCache().findFile(tile
.getFileCachePath(), false);
553 if (this.layer
.loadTile(this.tile
, tileURL
))
555 tile
.getPlaceNameService().unmarkResourceAbsent(tile
.getPlaceNameService().getTileNumber(
558 this.layer
.firePropertyChange(AVKey
.LAYER
, null, this);
562 // Assume that something's wrong with the file and delete it.
563 WorldWind
.dataFileCache().removeFile(tileURL
);
564 tile
.getPlaceNameService().markResourceAbsent(tile
.getPlaceNameService().getTileNumber(tile
.row
,
566 String message
= WorldWind
.retrieveErrMsg("generic.DeletedCorruptDataFile") + tileURL
;
567 WorldWind
.logger().log(Level
.FINE
, message
);
573 this.layer
.requestTile(this.layer
.downloadQueue
, this.tile
);
576 public String
toString()
578 return this.tile
.toString();
582 private static class DownloadPostProcessor
implements RetrievalPostProcessor
584 final PlaceNameLayer layer
;
587 private DownloadPostProcessor(PlaceNameLayer layer
, Tile tile
)
593 public java
.nio
.ByteBuffer
run(Retriever retriever
)
595 if (retriever
== null)
597 String msg
= WorldWind
.retrieveErrMsg("nullValue.RetrieverIsNull");
598 WorldWind
.logger().log(Level
.FINE
, msg
);
599 throw new IllegalArgumentException(msg
);
602 if (!retriever
.getState().equals(Retriever
.RETRIEVER_STATE_SUCCESSFUL
))
605 if (!(retriever
instanceof URLRetriever
))
610 if (retriever
instanceof HTTPRetriever
)
612 HTTPRetriever httpRetriever
= (HTTPRetriever
) retriever
;
613 if (httpRetriever
.getResponseCode() == java
.net
.HttpURLConnection
.HTTP_NO_CONTENT
)
615 // Mark tile as missing to avoid further attempts
616 tile
.getPlaceNameService().markResourceAbsent(tile
.getPlaceNameService().getTileNumber(tile
.row
,
622 URLRetriever urlRetriever
= (URLRetriever
) retriever
;
623 java
.nio
.ByteBuffer buffer
= urlRetriever
.getBuffer();
627 final java
.io
.File cacheFile
= WorldWind
.dataFileCache().newFile(this.tile
.getFileCachePath());
628 if (cacheFile
== null)
630 String msg
= WorldWind
.retrieveErrMsg("generic.CantCreateCacheFile")
631 + this.tile
.getFileCachePath();
632 WorldWind
.logger().log(Level
.FINE
, msg
);
636 if (cacheFile
.exists())
637 return buffer
; // info is already here; don't need to do anything
641 WWIO
.saveBuffer(buffer
, cacheFile
);
642 this.layer
.firePropertyChange(AVKey
.LAYER
, null, this);
647 catch (java
.io
.IOException e
)
649 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionSavingRetrievedFile");
650 WorldWind
.logger().log(Level
.FINE
, message
+ this.tile
.getFileCachePath(), e
);
657 private static class GMLPlaceNameSAXHandler
extends org
.xml
.sax
.helpers
.DefaultHandler
659 static final String GML_FEATURE_MEMBER
= "gml:featureMember";
660 static final String TOPP_FULL_NAME_ND
= "topp:full_name_nd";
661 static final String TOPP_LATITUDE
= "topp:latitude";
662 static final String TOPP_LONGITUDE
= "topp:longitude";
663 final LinkedList
<String
> qNameStack
= new LinkedList
<String
>();
664 boolean inBeginEndPair
= false;
665 StringBuilder latBuffer
= new StringBuilder();
666 StringBuilder lonBuffer
= new StringBuilder();
668 StringBuilder textArray
= new StringBuilder();
669 int[] textIndexArray
= new int[16];
670 double[] latlonArray
= new double[16];
673 GMLPlaceNameSAXHandler()
677 PlaceNameChunk
createPlaceNameChunk(PlaceNameService service
)
679 return new PlaceNameChunk(service
, this.textArray
, this.textIndexArray
, this.latlonArray
, this.numEntries
);
684 int textIndex
= this.textArray
.length();
685 this.textIndexArray
= append(this.textIndexArray
, this.numEntries
, textIndex
);
686 this.inBeginEndPair
= true;
691 double lat
= this.parseDouble(this.latBuffer
);
692 double lon
= this.parseDouble(this.lonBuffer
);
693 int numLatLon
= 2 * this.numEntries
;
694 this.latlonArray
= this.append(this.latlonArray
, numLatLon
, lat
);
696 this.latlonArray
= this.append(this.latlonArray
, numLatLon
, lon
);
698 this.latBuffer
.delete(0, this.latBuffer
.length());
699 this.lonBuffer
.delete(0, this.lonBuffer
.length());
700 this.inBeginEndPair
= false;
704 double parseDouble(StringBuilder sb
)
709 value
= Double
.parseDouble(sb
.toString());
711 catch (NumberFormatException e
)
713 String msg
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
714 WorldWind
.logger().log(Level
.FINE
, msg
, e
);
719 int[] append(int[] array
, int index
, int value
)
721 if (index
>= array
.length
)
722 array
= this.resizeArray(array
);
723 array
[index
] = value
;
727 int[] resizeArray(int[] oldArray
)
729 int newSize
= 2 * oldArray
.length
;
730 int[] newArray
= new int[newSize
];
731 System
.arraycopy(oldArray
, 0, newArray
, 0, oldArray
.length
);
735 double[] append(double[] array
, int index
, double value
)
737 if (index
>= array
.length
)
738 array
= this.resizeArray(array
);
739 array
[index
] = value
;
743 double[] resizeArray(double[] oldArray
)
745 int newSize
= 2 * oldArray
.length
;
746 double[] newArray
= new double[newSize
];
747 System
.arraycopy(oldArray
, 0, newArray
, 0, oldArray
.length
);
751 public void characters(char ch
[], int start
, int length
)
753 if (!this.inBeginEndPair
)
756 String top
= this.qNameStack
.getFirst();
758 StringBuilder sb
= null;
759 if (TOPP_LATITUDE
== top
)
761 else if (TOPP_LONGITUDE
== top
)
763 else if (TOPP_FULL_NAME_ND
== top
)
767 sb
.append(ch
, start
, length
);
770 public void startElement(String uri
, String localName
, String qName
, org
.xml
.sax
.Attributes attributes
)
772 String internQName
= qName
.intern();
773 // Don't validate uri, localName or attributes because they aren't used.
774 if (GML_FEATURE_MEMBER
== internQName
)
776 this.qNameStack
.addFirst(qName
);
779 public void endElement(String uri
, String localName
, String qName
)
781 String internQName
= qName
.intern();
782 // Don't validate uri or localName because they aren't used.
783 if (GML_FEATURE_MEMBER
== internQName
)
785 this.qNameStack
.removeFirst();
789 private boolean loadTile(Tile tile
, java
.net
.URL url
)
791 PlaceNameChunk placeNameChunk
= readTile(tile
, url
);
792 if (placeNameChunk
== null)
795 WorldWind
.memoryCache().add(tile
, placeNameChunk
);
799 private static PlaceNameChunk
readTile(Tile tile
, java
.net
.URL url
)
801 java
.io
.InputStream is
= null;
805 String path
= url
.getFile();
806 path
= path
.replaceAll("%20", " "); // TODO: find a better way to get a path usable by FileInputStream
808 java
.io
.FileInputStream fis
= new java
.io
.FileInputStream(path
);
809 java
.io
.BufferedInputStream buf
= new java
.io
.BufferedInputStream(fis
);
810 is
= new java
.util
.zip
.GZIPInputStream(buf
);
812 GMLPlaceNameSAXHandler handler
= new GMLPlaceNameSAXHandler();
813 javax
.xml
.parsers
.SAXParserFactory
.newInstance().newSAXParser().parse(is
, handler
);
814 return handler
.createPlaceNameChunk(tile
.getPlaceNameService());
818 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
819 WorldWind
.logger().log(Level
.FINE
, message
, e
);
828 catch (java
.io
.IOException e
)
830 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToReadFile");
831 WorldWind
.logger().log(Level
.FINE
, message
, e
);
838 private void requestTile(Queue
<Tile
> queue
, Tile tile
)
840 if (tile
.getPlaceNameService().isResourceAbsent(tile
.getPlaceNameService().getTileNumber(
841 tile
.row
, tile
.column
)))
843 if (!queue
.contains(tile
))
847 private void sendRequests()
850 // Send threaded read tasks.
851 while (!WorldWind
.threadedTaskService().isFull() && (tile
= this.readQueue
.poll()) != null)
853 WorldWind
.threadedTaskService().addTask(new RequestTask(this, tile
));
855 // Send retriever tasks.
856 while (!WorldWind
.retrievalService().isFull() && (tile
= this.downloadQueue
.poll()) != null)
861 url
= tile
.getRequestURL();
863 catch (java
.net
.MalformedURLException e
)
865 String message
= WorldWind
.retrieveErrMsg("layers.PlaceNameLayer.ExceptionAttemptingToDownloadFile");
866 WorldWind
.logger().log(Level
.FINE
, message
+ tile
, e
);
869 WorldWind
.retrievalService().runRetriever(new HTTPRetriever(url
, new DownloadPostProcessor(this, tile
)));