protected boolean loadTile(Tile tile, java.net.URL url) {
    if (WWIO.isFileOutOfDate(url, this.placeNameServiceSet.getExpiryTime())) {
      // The file has expired. Delete it then request download of newer.
      this.getDataFileStore().removeFile(url);
      String message = Logging.getMessage("generic.DataFileExpired", url);
      Logging.logger().fine(message);
      return false;
    }

    PlaceNameChunk tileData;
    synchronized (this.fileLock) {
      tileData = readTileData(tile, url);
    }

    if (tileData == null) {
      // Assume that something's wrong with the file and delete it.
      this.getDataFileStore().removeFile(url);
      tile.getPlaceNameService()
          .markResourceAbsent(tile.getPlaceNameService().getTileNumber(tile.row, tile.column));
      String message = Logging.getMessage("generic.DeletedCorruptDataFile", url);
      Logging.logger().fine(message);
      return false;
    }

    tile.setDataChunk(tileData);
    WorldWind.getMemoryCache(Tile.class.getName()).add(tile.getFileCachePath(), tile);
    return true;
  }
    public void run() {
      if (this.tile.isTileInMemoryWithData()) return;

      final java.net.URL tileURL =
          this.layer.getDataFileStore().findFile(tile.getFileCachePath(), false);
      if (tileURL != null) {
        if (this.layer.loadTile(this.tile, tileURL)) {
          tile.getPlaceNameService()
              .unmarkResourceAbsent(
                  tile.getPlaceNameService().getTileNumber(tile.row, tile.column));
          this.layer.firePropertyChange(AVKey.LAYER, null, this);
          return;
        }
      }

      this.layer.downloadTile(this.tile);
    }
  protected void drawOrRequestTile(
      DrawContext dc,
      Tile tile,
      double minDisplayDistanceSquared,
      double maxDisplayDistanceSquared) {
    if (!isTileVisible(dc, tile, minDisplayDistanceSquared, maxDisplayDistanceSquared)) return;

    if (tile.isTileInMemoryWithData()) {
      PlaceNameChunk placeNameChunk = tile.getDataChunk();
      if (placeNameChunk.numEntries > 0) {
        Iterable<GeographicText> renderIter = placeNameChunk.makeIterable(dc);
        this.placeNameRenderer.render(dc, renderIter);
      }
      return;
    }

    // Tile's data isn't available, so request it
    if (!tile.getPlaceNameService()
        .isResourceAbsent(tile.getPlaceNameService().getTileNumber(tile.row, tile.column))) {
      this.requestTile(dc, tile);
    }
  }
  protected static PlaceNameChunk readTileData(Tile tile, java.net.URL url) {
    java.io.InputStream is = null;

    try {
      String path = url.getFile();
      path =
          path.replaceAll(
              "%20", " "); // TODO: find a better way to get a path usable by FileInputStream

      java.io.FileInputStream fis = new java.io.FileInputStream(path);
      java.io.BufferedInputStream buf = new java.io.BufferedInputStream(fis);
      is = new java.util.zip.GZIPInputStream(buf);

      GMLPlaceNameSAXHandler handler = new GMLPlaceNameSAXHandler();
      javax.xml.parsers.SAXParserFactory.newInstance().newSAXParser().parse(is, handler);
      return handler.createPlaceNameChunk(tile.getPlaceNameService());
    } catch (Exception e) {
      // todo log actual error
      Logging.logger()
          .log(
              Level.FINE,
              Logging.getMessage(
                  "layers.PlaceNameLayer.ExceptionAttemptingToReadFile", url.toString()),
              e);
    } finally {
      try {
        if (is != null) is.close();
      } catch (java.io.IOException e) {
        Logging.logger()
            .log(
                Level.FINE,
                Logging.getMessage(
                    "layers.PlaceNameLayer.ExceptionAttemptingToReadFile", url.toString()),
                e);
      }
    }

    return null;
  }