private BufferedImage requestImage(MercatorTextureTile tile, String mimeType)
      throws URISyntaxException {
    String pathBase = tile.getPath().substring(0, tile.getPath().lastIndexOf("."));
    String suffix = WWIO.makeSuffixForMimeType(mimeType);
    String path = pathBase + suffix;
    URL url = WorldWind.getDataFileStore().findFile(path, false);

    if (url == null) // image is not local
    return null;

    if (WWIO.isFileOutOfDate(url, tile.getLevel().getExpiryTime())) {
      // The file has expired. Delete it.
      WorldWind.getDataFileStore().removeFile(url);
      String message = Logging.getMessage("generic.DataFileExpired", url);
      Logging.logger().fine(message);
    } else {
      try {
        File imageFile = new File(url.toURI());
        BufferedImage image = ImageIO.read(imageFile);
        if (image == null) {
          String message = Logging.getMessage("generic.ImageReadFailed", imageFile);
          throw new RuntimeException(message);
        }

        this.levels.unmarkResourceAbsent(tile);
        return image;
      } catch (IOException e) {
        // Assume that something's wrong with the file and delete it.
        gov.nasa.worldwind.WorldWind.getDataFileStore().removeFile(url);
        this.levels.markResourceAbsent(tile);
        String message = Logging.getMessage("generic.DeletedCorruptDataFile", url);
        Logging.logger().info(message);
      }
    }

    return null;
  }
  private void addTileOrDescendants(DrawContext dc, MercatorTextureTile tile) {
    if (this.meetsRenderCriteria(dc, tile)) {
      this.addTile(dc, tile);
      return;
    }

    // The incoming tile does not meet the rendering criteria, so it must be subdivided and those
    // subdivisions tested against the criteria.

    // All tiles that meet the selection criteria are drawn, but some of those tiles will not have
    // textures associated with them either because their texture isn't loaded yet or because they
    // are finer grain than the layer has textures for. In these cases the tiles use the texture of
    // the closest ancestor that has a texture loaded. This ancestor is called the
    // currentResourceTile.
    // A texture transform is applied during rendering to align the sector's texture coordinates
    // with the
    // appropriate region of the ancestor's texture.

    MercatorTextureTile ancestorResource = null;

    try {
      // TODO: Revise this to reflect that the parent layer is only requested while the algorithm
      // continues
      // to search for the layer matching the criteria.
      // At this point the tile does not meet the render criteria but it may have its texture in
      // memory.
      // If so, register this tile as the resource tile. If not, then this tile will be the next
      // level
      // below a tile with texture in memory. So to provide progressive resolution increase, add
      // this tile
      // to the draw list. That will cause the tile to be drawn using its parent tile's texture, and
      // it will
      // cause it's texture to be requested. At some future call to this method the tile's texture
      // will be in
      // memory, it will not meet the render criteria, but will serve as the parent to a tile that
      // goes
      // through this same process as this method recurses. The result of all this is that a tile
      // isn't rendered
      // with its own texture unless all its parents have their textures loaded. In addition to
      // causing
      // progressive resolution increase, this ensures that the parents are available as the user
      // zooms out, and
      // therefore the layer remains visible until the user is zoomed out to the point the layer is
      // no longer
      // active.
      if (tile.isTextureInMemory(dc.getTextureCache()) || tile.getLevelNumber() == 0) {
        ancestorResource = this.currentResourceTile;
        this.currentResourceTile = tile;
      } else if (!tile.getLevel().isEmpty()) {
        //                this.addTile(dc, tile);
        //                return;

        // Issue a request for the parent before descending to the children.
        if (tile.getLevelNumber() < this.levels.getNumLevels()) {
          // Request only tiles with data associated at this level
          if (!this.levels.isResourceAbsent(tile)) this.requestTexture(dc, tile);
        }
      }

      MercatorTextureTile[] subTiles =
          tile.createSubTiles(this.levels.getLevel(tile.getLevelNumber() + 1));
      for (MercatorTextureTile child : subTiles) {
        if (this.isTileVisible(dc, child)) this.addTileOrDescendants(dc, child);
      }
    } finally {
      if (ancestorResource != null) // Pop this tile as the currentResource ancestor
      this.currentResourceTile = ancestorResource;
    }
  }