protected static boolean isTileVisible(
      DrawContext dc, Tile tile, double minDistanceSquared, double maxDistanceSquared) {
    if (!tile.getSector().intersects(dc.getVisibleSector())) return false;

    View view = dc.getView();
    Position eyePos = view.getEyePosition();
    if (eyePos == null) return false;

    Angle lat =
        clampAngle(
            eyePos.getLatitude(),
            tile.getSector().getMinLatitude(),
            tile.getSector().getMaxLatitude());
    Angle lon =
        clampAngle(
            eyePos.getLongitude(),
            tile.getSector().getMinLongitude(),
            tile.getSector().getMaxLongitude());
    Vec4 p = dc.getGlobe().computePointFromPosition(lat, lon, 0d);
    double distSquared = dc.getView().getEyePoint().distanceToSquared3(p);
    //noinspection RedundantIfStatement
    if (minDistanceSquared > distSquared || maxDistanceSquared < distSquared) return false;

    return true;
  }
  /**
   * Sets a specified texture's OpenGL <code>Texture</code> parameters.
   *
   * @param dc the current draw context.
   * @param texture the texture whose parameters to set.
   */
  protected void setTextureParameters(DrawContext dc, Texture texture) {
    // Enable the appropriate mip-mapping texture filters if the caller has specified that
    // mip-mapping should be
    // enabled, and the texture itself supports mip-mapping.
    boolean useMipMapFilter =
        this.useMipMaps
            && (this.getTextureData().getMipmapData() != null
                || texture.isUsingAutoMipmapGeneration());

    GL gl = dc.getGL();
    gl.glTexParameteri(
        GL.GL_TEXTURE_2D,
        GL.GL_TEXTURE_MIN_FILTER,
        useMipMapFilter ? GL.GL_LINEAR_MIPMAP_LINEAR : GL.GL_LINEAR);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);

    if (this.isUseAnisotropy() && useMipMapFilter) {
      double maxAnisotropy = dc.getGLRuntimeCapabilities().getMaxTextureAnisotropy();
      if (dc.getGLRuntimeCapabilities().isUseAnisotropicTextureFilter() && maxAnisotropy >= 2.0) {
        gl.glTexParameterf(
            GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, (float) maxAnisotropy);
      }
    }
  }
  protected static boolean isServiceVisible(DrawContext dc, PlaceNameService placeNameService) {
    //noinspection SimplifiableIfStatement
    if (!placeNameService.isEnabled()) return false;

    return (dc.getVisibleSector() != null)
        && placeNameService.getMaskingSector().intersects(dc.getVisibleSector());
    //
    //        return
    // placeNameService.getExtent(dc).intersects(dc.getView().getFrustumInModelCoordinates());
  }
  protected static boolean isNameVisible(
      DrawContext dc, PlaceNameService service, Position namePosition) {
    double elevation = dc.getVerticalExaggeration() * namePosition.getElevation();
    Vec4 namePoint =
        dc.getGlobe()
            .computePointFromPosition(
                namePosition.getLatitude(), namePosition.getLongitude(), elevation);
    Vec4 eyeVec = dc.getView().getEyePoint();

    double dist = eyeVec.distanceTo3(namePoint);
    return dist >= service.getMinDisplayDistance() && dist <= service.getMaxDisplayDistance();
  }
  protected void requestTile(DrawContext dc, Tile tile) {
    Vec4 centroid = dc.getGlobe().computePointFromPosition(tile.getSector().getCentroid(), 0);
    if (this.getReferencePoint() != null)
      tile.setPriority(centroid.distanceTo3(this.getReferencePoint()));

    RequestTask task = new RequestTask(tile, this);
    this.getRequestQ().add(task);
  }
  /**
   * If this instance's image source is a <code>BufferedImage</code>, creates and returns the
   * texture, otherwise creates a task in a separate thread to retrieve it from its local or remote
   * location.
   *
   * @param dc the current draw context.
   * @return the new texture, or null if the texture is not yet available.
   */
  protected Texture requestTexture(DrawContext dc) {
    if (this.isBufferedImageSource()) return this.makeBufferedImageTexture(dc);

    if (this.getTextureData() != null && this.getTexture(dc) == null)
      return this.makeTextureFromTextureData(dc);

    if (WorldWind.getTaskService().isFull()) return null;

    Runnable task = this.createRequestTask();
    if (WorldWind.getTaskService().contains(task)) return null;

    // Use either the current layer or the layer list as the listener to notify when the request
    // completes. The
    // latter is used when the image source is requested during ordered rendering and the current
    // layer is null.
    this.listener = dc.getCurrentLayer() != null ? dc.getCurrentLayer() : dc.getLayers();

    WorldWind.getTaskService().addTask(task);

    return null;
  }
  /**
   * Returns the {@link Texture} associated with this instance.
   *
   * @param dc the current draw context.
   * @return this instance's texture, or null if the texture does not currently exist.
   */
  protected Texture getTexture(DrawContext dc) {
    if (this.getImageSource() == null) return null;

    Texture texture = dc.getTextureCache().getTexture(this.getImageSource());

    if (this.width == null && texture != null) {
      this.width = texture.getWidth();
      this.height = texture.getHeight();
      this.texCoords = texture.getImageTexCoords();
    }

    return texture;
  }
    protected boolean isNavSectorVisible(
        DrawContext dc, double minDistanceSquared, double maxDistanceSquared) {
      if (!navSector.intersects(dc.getVisibleSector())) return false;

      View view = dc.getView();
      Position eyePos = view.getEyePosition();
      if (eyePos == null) return false;

      // check for eyePos over globe
      if (Double.isNaN(eyePos.getLatitude().getDegrees())
          || Double.isNaN(eyePos.getLongitude().getDegrees())) return false;

      Angle lat =
          clampAngle(eyePos.getLatitude(), navSector.getMinLatitude(), navSector.getMaxLatitude());
      Angle lon =
          clampAngle(
              eyePos.getLongitude(), navSector.getMinLongitude(), navSector.getMaxLongitude());
      Vec4 p = dc.getGlobe().computePointFromPosition(lat, lon, 0d);
      double distSquared = dc.getView().getEyePoint().distanceToSquared3(p);
      //noinspection RedundantIfStatement
      if (minDistanceSquared > distSquared || maxDistanceSquared < distSquared) return false;

      return true;
    }
  protected Vec4 computeReferencePoint(DrawContext dc) {
    if (dc.getViewportCenterPosition() != null)
      return dc.getGlobe().computePointFromPosition(dc.getViewportCenterPosition());

    java.awt.geom.Rectangle2D viewport = dc.getView().getViewport();
    int x = (int) viewport.getWidth() / 2;
    for (int y = (int) (0.5 * viewport.getHeight()); y >= 0; y--) {
      Position pos = dc.getView().computePositionFromScreenPoint(x, y);
      if (pos == null) continue;

      return dc.getGlobe().computePointFromPosition(pos.getLatitude(), pos.getLongitude(), 0d);
    }

    return null;
  }
  /**
   * Creates a {@link Texture} from this instance's {@link TextureData} if the <code>TextureData
   * </code> exists.
   *
   * @param dc the current draw context.
   * @return the newly created texture, or null if this instance has no current <code>TextureData
   *     </code> or if texture creation failed.
   */
  protected Texture makeTextureFromTextureData(DrawContext dc) {
    if (dc == null) {
      String message = Logging.getMessage("nullValue.DrawContextIsNull");
      Logging.logger().severe(message);
      throw new IllegalStateException(message);
    }

    if (this.getTextureData()
        == null) // texture not in cache yet texture data is null, can't initialize
    {
      String msg = Logging.getMessage("nullValue.TextureDataIsNull");
      Logging.logger().severe(msg);
      throw new IllegalStateException(msg);
    }

    try {
      Texture texture = TextureIO.newTexture(this.getTextureData());
      if (texture == null) {
        this.textureInitializationFailed = true;
        return null;
      }

      this.width = texture.getWidth();
      this.height = texture.getHeight();
      this.texCoords = texture.getImageTexCoords();

      this.setTextureParameters(dc, texture);

      // Cache the texture and release the texture data.
      dc.getTextureCache().put(this.getImageSource(), texture);
      this.setTextureData(null);

      return texture;
    } catch (Exception e) {
      String name =
          this.isBufferedImageSource() ? "BufferedImage" : this.getImageSource().toString();
      String msg = Logging.getMessage("generic.ExceptionAttemptingToCreateTexture", name);
      Logging.logger().log(java.util.logging.Level.SEVERE, msg, e);
      return null;
    }
  }