private void loadAllTopLevelTextures(DrawContext dc) {
    for (MercatorTextureTile tile : this.topLevels) {
      if (!tile.isTextureInMemory(dc.getTextureCache())) this.forceTextureLoad(tile);
    }

    this.levelZeroLoaded = true;
  }
    public ByteBuffer run(Retriever retriever) {
      if (!retriever.getState().equals(Retriever.RETRIEVER_STATE_SUCCESSFUL)) return null;

      HTTPRetriever htr = (HTTPRetriever) retriever;
      if (htr.getResponseCode() == HttpURLConnection.HTTP_NO_CONTENT) {
        // Mark tile as missing to avoid excessive attempts
        MercatorTiledImageLayer.this.levels.markResourceAbsent(tile);
        return null;
      }

      if (htr.getResponseCode() != HttpURLConnection.HTTP_OK) return null;

      URLRetriever r = (URLRetriever) retriever;
      ByteBuffer buffer = r.getBuffer();

      String suffix = WWIO.makeSuffixForMimeType(htr.getContentType());
      if (suffix == null) {
        return null; // TODO: log error
      }

      String path = tile.getPath().substring(0, tile.getPath().lastIndexOf("."));
      path += suffix;

      final File outFile = WorldWind.getDataFileStore().newFile(path);
      if (outFile == null) return null;

      try {
        WWIO.saveBuffer(buffer, outFile);
        return buffer;
      } catch (IOException e) {
        e.printStackTrace(); // TODO: log error
        return null;
      }
    }
  private void drawTileIDs(DrawContext dc, ArrayList<MercatorTextureTile> tiles) {
    java.awt.Rectangle viewport = dc.getView().getViewport();
    if (this.textRenderer == null) {
      this.textRenderer = new TextRenderer(java.awt.Font.decode("Arial-Plain-13"), true, true);
      this.textRenderer.setUseVertexArrays(false);
    }

    dc.getGL().glDisable(GL.GL_DEPTH_TEST);
    dc.getGL().glDisable(GL.GL_BLEND);
    dc.getGL().glDisable(GL.GL_TEXTURE_2D);

    this.textRenderer.setColor(java.awt.Color.YELLOW);
    this.textRenderer.beginRendering(viewport.width, viewport.height);
    for (MercatorTextureTile tile : tiles) {
      String tileLabel = tile.getLabel();

      if (tile.getFallbackTile() != null) tileLabel += "/" + tile.getFallbackTile().getLabel();

      LatLon ll = tile.getSector().getCentroid();
      Vec4 pt =
          dc.getGlobe()
              .computePointFromPosition(
                  ll.getLatitude(),
                  ll.getLongitude(),
                  dc.getGlobe().getElevation(ll.getLatitude(), ll.getLongitude()));
      pt = dc.getView().project(pt);
      this.textRenderer.draw(tileLabel, (int) pt.x, (int) pt.y);
    }
    this.textRenderer.endRendering();
  }
  private void drawBoundingVolumes(DrawContext dc, ArrayList<MercatorTextureTile> tiles) {
    float[] previousColor = new float[4];
    dc.getGL().glGetFloatv(GL.GL_CURRENT_COLOR, previousColor, 0);
    dc.getGL().glColor3d(0, 1, 0);

    for (MercatorTextureTile tile : tiles) {
      ((Cylinder) tile.getExtent(dc)).render(dc);
    }

    Cylinder c =
        dc.getGlobe()
            .computeBoundingCylinder(dc.getVerticalExaggeration(), this.levels.getSector());
    dc.getGL().glColor3d(1, 1, 0);
    c.render(dc);

    dc.getGL().glColor4fv(previousColor, 0);
  }
 private boolean isTileVisible(DrawContext dc, MercatorTextureTile tile) {
   //        if (!(tile.getExtent(dc).intersects(dc.getView().getFrustumInModelCoordinates())
   //            && (dc.getVisibleSector() == null ||
   // dc.getVisibleSector().intersects(tile.getSector()))))
   //            return false;
   //
   //        Position eyePos = dc.getView().getEyePosition();
   //        LatLon centroid = tile.getSector().getCentroid();
   //        Angle d = LatLon.greatCircleDistance(eyePos.getLatLon(), centroid);
   //        if ((!tile.getLevelName().equals("0")) &&
   // d.compareTo(tile.getSector().getDeltaLat().multiply(2.5)) == 1)
   //            return false;
   //
   //        return true;
   //
   return tile.getExtent(dc).intersects(dc.getView().getFrustumInModelCoordinates())
       && (dc.getVisibleSector() == null || dc.getVisibleSector().intersects(tile.getSector()));
 }
  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;
  }
    public int compare(MercatorTextureTile ta, MercatorTextureTile tb) {
      int la =
          ta.getFallbackTile() == null
              ? ta.getLevelNumber()
              : ta.getFallbackTile().getLevelNumber();
      int lb =
          tb.getFallbackTile() == null
              ? tb.getLevelNumber()
              : tb.getFallbackTile().getLevelNumber();

      return la < lb ? -1 : la == lb ? 0 : 1;
    }
  private BufferedImage getImage(MercatorTextureTile tile, String mimeType) throws Exception {
    // Read the image from disk.
    BufferedImage image = this.requestImage(tile, mimeType);
    if (image != null) return image;

    // Retrieve it from the net since it's not on disk.
    this.downloadImage(tile, mimeType);

    // Try to read from disk again after retrieving it from the net.
    image = this.requestImage(tile, mimeType);
    if (image == null) {
      String message =
          Logging.getMessage("layers.TiledImageLayer.ImageUnavailable", tile.getPath());
      throw new RuntimeException(message);
    }

    return image;
  }
  private void downloadImage(final MercatorTextureTile tile, String mimeType) throws Exception {
    //        System.out.println(tile.getPath());
    final URL resourceURL = tile.getResourceURL(mimeType);
    Retriever retriever;

    String protocol = resourceURL.getProtocol();

    if ("http".equalsIgnoreCase(protocol)) {
      retriever = new HTTPRetriever(resourceURL, new HttpRetrievalPostProcessor(tile));
    } else {
      String message =
          Logging.getMessage("layers.TextureLayer.UnknownRetrievalProtocol", resourceURL);
      throw new RuntimeException(message);
    }

    retriever.setConnectTimeout(10000);
    retriever.setReadTimeout(20000);
    retriever.call();
  }
  private void addTile(DrawContext dc, MercatorTextureTile tile) {
    tile.setFallbackTile(null);

    if (tile.isTextureInMemory(dc.getTextureCache())) {
      //            System.out.printf("Sector %s, min = %f, max = %f\n", tile.getSector(),
      //                dc.getGlobe().getMinElevation(tile.getSector()),
      // dc.getGlobe().getMaxElevation(tile.getSector()));
      this.addTileToCurrent(tile);
      return;
    }

    // Level 0 loads may be forced
    if (tile.getLevelNumber() == 0
        && this.forceLevelZeroLoads
        && !tile.isTextureInMemory(dc.getTextureCache())) {
      this.forceTextureLoad(tile);
      if (tile.isTextureInMemory(dc.getTextureCache())) {
        this.addTileToCurrent(tile);
        return;
      }
    }

    // Tile's texture isn't available, so request it
    if (tile.getLevelNumber() < this.levels.getNumLevels()) {
      // Request only tiles with data associated at this level
      if (!this.levels.isResourceAbsent(tile)) this.requestTexture(dc, tile);
    }

    // Set up to use the currentResource tile's texture
    if (this.currentResourceTile != null) {
      if (this.currentResourceTile.getLevelNumber() == 0
          && this.forceLevelZeroLoads
          && !this.currentResourceTile.isTextureInMemory(dc.getTextureCache())
          && !this.currentResourceTile.isTextureInMemory(dc.getTextureCache()))
        this.forceTextureLoad(this.currentResourceTile);

      if (this.currentResourceTile.isTextureInMemory(dc.getTextureCache())) {
        tile.setFallbackTile(currentResourceTile);
        this.addTileToCurrent(tile);
      }
    }
  }
  public BufferedImage composeImageForSector(
      Sector sector,
      int imageWidth,
      int imageHeight,
      int levelNumber,
      String mimeType,
      boolean abortOnError,
      BufferedImage image) {
    if (sector == null) {
      String message = Logging.getMessage("nullValue.SectorIsNull");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    if (levelNumber < 0) {
      levelNumber = this.levels.getLastLevel().getLevelNumber();
    } else if (levelNumber > this.levels.getLastLevel().getLevelNumber()) {
      Logging.logger()
          .warning(
              Logging.getMessage(
                  "generic.LevelRequestedGreaterThanMaxLevel",
                  levelNumber,
                  this.levels.getLastLevel().getLevelNumber()));
      levelNumber = this.levels.getLastLevel().getLevelNumber();
    }

    MercatorTextureTile[][] tiles = this.getTilesInSector(sector, levelNumber);

    if (tiles.length == 0 || tiles[0].length == 0) {
      Logging.logger().severe(Logging.getMessage("layers.TiledImageLayer.NoImagesAvailable"));
      return null;
    }

    if (image == null)
      image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);

    Graphics2D g = image.createGraphics();

    for (MercatorTextureTile[] row : tiles) {
      for (MercatorTextureTile tile : row) {
        if (tile == null) continue;

        BufferedImage tileImage;
        try {
          tileImage = this.getImage(tile, mimeType);

          double sh =
              ((double) imageHeight / (double) tileImage.getHeight())
                  * (tile.getSector().getDeltaLat().divide(sector.getDeltaLat()));
          double sw =
              ((double) imageWidth / (double) tileImage.getWidth())
                  * (tile.getSector().getDeltaLon().divide(sector.getDeltaLon()));

          double dh =
              imageHeight
                  * (-tile.getSector().getMaxLatitude().subtract(sector.getMaxLatitude()).degrees
                      / sector.getDeltaLat().degrees);
          double dw =
              imageWidth
                  * (tile.getSector().getMinLongitude().subtract(sector.getMinLongitude()).degrees
                      / sector.getDeltaLon().degrees);

          AffineTransform txf = g.getTransform();
          g.translate(dw, dh);
          g.scale(sw, sh);
          g.drawImage(tileImage, 0, 0, null);
          g.setTransform(txf);
        } catch (Exception e) {
          if (abortOnError) throw new RuntimeException(e);

          String message =
              Logging.getMessage("generic.ExceptionWhileRequestingImage", tile.getPath());
          Logging.logger().log(java.util.logging.Level.WARNING, message, e);
        }
      }
    }

    return image;
  }
 private boolean meetsRenderCriteria(DrawContext dc, MercatorTextureTile tile) {
   return this.levels.isFinalLevel(tile.getLevelNumber()) || !needToSplit(dc, tile.getSector());
 }
  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;
    }
  }