protected void onHorizontalTranslateRel(
      Angle forwardChange, Angle sideChange, ViewInputAttributes.ActionAttributes actionAttribs) {
    View view = this.getView();
    if (view == null) // include this test to ensure any derived implementation performs it
    {
      return;
    }

    if (forwardChange.equals(Angle.ZERO) && sideChange.equals(Angle.ZERO)) {
      return;
    }

    if (view instanceof BasicFlyView) {

      Vec4 forward = view.getForwardVector();
      Vec4 up = view.getUpVector();
      Vec4 side = forward.transformBy3(Matrix.fromAxisAngle(Angle.fromDegrees(90), up));

      forward = forward.multiply3(forwardChange.getDegrees());
      side = side.multiply3(sideChange.getDegrees());
      Vec4 eyePoint = view.getEyePoint();
      eyePoint = eyePoint.add3(forward.add3(side));
      Position newPosition = view.getGlobe().computePositionFromPoint(eyePoint);

      this.setEyePosition(this.uiAnimControl, view, newPosition, actionAttribs);
      view.firePropertyChange(AVKey.VIEW, null, view);
    }
  }
Esempio n. 2
0
  /**
   * Compute the positions of the arrow head of the graphic's legs.
   *
   * @param dc Current draw context
   * @param base Position of the arrow's starting point.
   * @param tip Position of the arrow head tip.
   * @param arrowLength Length of the arrowhead as a fraction of the total line length.
   * @param arrowAngle Angle of the arrow head.
   * @return Positions required to draw the arrow head.
   */
  protected List<Position> computeArrowheadPositions(
      DrawContext dc, Position base, Position tip, double arrowLength, Angle arrowAngle) {
    // Build a triangle to represent the arrowhead. The triangle is built from two vectors, one
    // parallel to the
    // segment, and one perpendicular to it.

    Globe globe = dc.getGlobe();

    Vec4 ptA = globe.computePointFromPosition(base);
    Vec4 ptB = globe.computePointFromPosition(tip);

    // Compute parallel component
    Vec4 parallel = ptA.subtract3(ptB);

    Vec4 surfaceNormal = globe.computeSurfaceNormalAtPoint(ptB);

    // Compute perpendicular component
    Vec4 perpendicular = surfaceNormal.cross3(parallel);

    double finalArrowLength = arrowLength * parallel.getLength3();
    double arrowHalfWidth = finalArrowLength * arrowAngle.tanHalfAngle();

    perpendicular = perpendicular.normalize3().multiply3(arrowHalfWidth);
    parallel = parallel.normalize3().multiply3(finalArrowLength);

    // Compute geometry of direction arrow
    Vec4 vertex1 = ptB.add3(parallel).add3(perpendicular);
    Vec4 vertex2 = ptB.add3(parallel).subtract3(perpendicular);

    return TacticalGraphicUtil.asPositionList(globe, vertex1, vertex2, ptB);
  }
  protected void assembleHeightControlPoints() {
    if (this.controlPoints.size() < 2) return;

    // Add one control point for the height between the first and second vertices.
    // TODO: ensure that this control point is visible
    Position firstVertex = this.controlPoints.get(0).getPosition();
    Position secondVertex = this.controlPoints.get(1).getPosition();

    Globe globe = this.wwd.getModel().getGlobe();

    // Get cartesian points for the vertices
    Vec4 firstPoint = globe.computePointFromPosition(firstVertex);
    Vec4 secondPoint = globe.computePointFromPosition(secondVertex);

    // Find the midpoint of the line segment that connects the vertices
    Vec4 halfwayPoint = firstPoint.add3(secondPoint).divide3(2.0);

    Position halfwayPosition = globe.computePositionFromPoint(halfwayPoint);

    this.controlPoints.add(
        new ControlPointMarker(
            CHANGE_HEIGHT_ACTION,
            halfwayPosition,
            halfwayPoint,
            this.heightControlAttributes,
            this.controlPoints.size()));
  }
  protected Vec4[] calculateNormals(int width, int height, Vec4[] verts, int padding) {
    int padding2 = padding * 2;

    if (verts.length != (width + padding2) * (height + padding2))
      throw new IllegalStateException("Illegal vertices length");

    Vec4[] norms = new Vec4[width * height];
    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        //   v2
        //   |
        // v1-v0-v3
        //   |
        //   v4

        Vec4 v0 =
            verts[getArrayIndex(width + padding2, height + padding2, x + padding, y + padding)];
        if (v0 != null) {
          Vec4 v1 =
              verts[
                  getArrayIndex(width + padding2, height + padding2, x + padding - 1, y + padding)];
          Vec4 v2 =
              verts[
                  getArrayIndex(width + padding2, height + padding2, x + padding, y + padding - 1)];
          Vec4 v3 =
              verts[
                  getArrayIndex(width + padding2, height + padding2, x + padding + 1, y + padding)];
          Vec4 v4 =
              verts[
                  getArrayIndex(width + padding2, height + padding2, x + padding, y + padding + 1)];

          Vec4[] normals = new Vec4[4];
          normals[0] =
              v1 != null && v2 != null
                  ? v1.subtract3(v0).cross3(v0.subtract3(v2)).normalize3()
                  : null;
          normals[1] =
              v2 != null && v3 != null
                  ? v2.subtract3(v0).cross3(v0.subtract3(v3)).normalize3()
                  : null;
          normals[2] =
              v3 != null && v4 != null
                  ? v3.subtract3(v0).cross3(v0.subtract3(v4)).normalize3()
                  : null;
          normals[3] =
              v4 != null && v1 != null
                  ? v4.subtract3(v0).cross3(v0.subtract3(v1)).normalize3()
                  : null;
          Vec4 normal = Vec4.ZERO;
          for (Vec4 n : normals) {
            if (n != null) normal = normal.add3(n);
          }
          if (normal != Vec4.ZERO) {
            norms[getArrayIndex(width, height, x, y)] = normal.normalize3();
          }
        }
      }
    }
    return norms;
  }
  protected void assembleVertexControlPoints(DrawContext dc) {
    Terrain terrain = dc.getTerrain();
    ExtrudedPolygon polygon = this.getPolygon();

    Position refPos = polygon.getReferencePosition();
    Vec4 refPoint = terrain.getSurfacePoint(refPos.getLatitude(), refPos.getLongitude(), 0);

    int altitudeMode = polygon.getAltitudeMode();
    double height = polygon.getHeight();

    Vec4 vaa = null;
    double vaaLength = 0; // used to compute independent length of each cap vertex
    double vaLength = 0;

    int i = 0;
    for (LatLon location : polygon.getOuterBoundary()) {
      Vec4 vert;

      // Compute the top/cap point.
      if (altitudeMode == WorldWind.CONSTANT || !(location instanceof Position)) {
        if (vaa == null) {
          // Compute the vector lengths of the top and bottom points at the reference position.
          vaa = refPoint.multiply3(height / refPoint.getLength3());
          vaaLength = vaa.getLength3();
          vaLength = refPoint.getLength3();
        }

        // Compute the bottom point, which is on the terrain.
        vert = terrain.getSurfacePoint(location.getLatitude(), location.getLongitude(), 0);

        double delta = vaLength - vert.dot3(refPoint) / vaLength;
        vert = vert.add3(vaa.multiply3(1d + delta / vaaLength));
      } else if (altitudeMode == WorldWind.RELATIVE_TO_GROUND) {
        vert =
            terrain.getSurfacePoint(
                location.getLatitude(),
                location.getLongitude(),
                ((Position) location).getAltitude());
      } else // WorldWind.ABSOLUTE
      {
        vert =
            terrain
                .getGlobe()
                .computePointFromPosition(
                    location.getLatitude(),
                    location.getLongitude(),
                    ((Position) location).getAltitude() * terrain.getVerticalExaggeration());
      }

      Position vertexPosition = this.wwd.getModel().getGlobe().computePositionFromPoint(vert);

      this.controlPoints.add(
          new ControlPointMarker(
              MOVE_VERTEX_ACTION, vertexPosition, vert, this.vertexControlAttributes, i));
      i++;
    }
  }
Esempio n. 6
0
  /**
   * Determine the positions that make up the arrowhead.
   *
   * @param dc Current draw context.
   * @param startPosition Position of the arrow's base.
   * @param endPosition Position of the arrow head tip.
   * @return Positions that define the arrowhead.
   */
  protected List<Position> computeArrowheadPositions(
      DrawContext dc, Position startPosition, Position endPosition) {
    Globe globe = dc.getGlobe();

    // Arrowhead looks like this:
    //                  _
    //        A\         | 1/2 width
    // ________B\       _|
    // Pt. 1    /
    //        C/
    //         | |
    //      Length

    Vec4 p1 = globe.computePointFromPosition(startPosition);
    Vec4 pB = globe.computePointFromPosition(endPosition);

    // Find vector in the direction of the arrow
    Vec4 vB1 = p1.subtract3(pB);

    double arrowLengthFraction = this.getArrowLength();

    // Find the point at the base of the arrowhead
    Vec4 arrowBase = pB.add3(vB1.multiply3(arrowLengthFraction));

    Vec4 normal = globe.computeSurfaceNormalAtPoint(arrowBase);

    // Compute the length of the arrowhead
    double arrowLength = vB1.getLength3() * arrowLengthFraction;
    double arrowHalfWidth = arrowLength * this.getArrowAngle().tanHalfAngle();

    // Compute a vector perpendicular to the segment and the normal vector
    Vec4 perpendicular = vB1.cross3(normal);
    perpendicular = perpendicular.normalize3().multiply3(arrowHalfWidth);

    // Find points A and C
    Vec4 pA = arrowBase.add3(perpendicular);
    Vec4 pC = arrowBase.subtract3(perpendicular);

    return TacticalGraphicUtil.asPositionList(globe, pA, pB, pC);
  }
Esempio n. 7
0
  protected void addToolTip(DrawContext dc, WWIcon icon, Vec4 iconPoint) {
    if (icon.getToolTipFont() == null && icon.getToolTipText() == null) return;

    Vec4 screenPoint = dc.getView().project(iconPoint);
    if (screenPoint == null) return;

    if (icon.getToolTipOffset() != null) screenPoint = screenPoint.add3(icon.getToolTipOffset());

    OrderedText tip =
        new OrderedText(
            icon.getToolTipText(),
            icon.getToolTipFont(),
            screenPoint,
            icon.getToolTipTextColor(),
            0d);
    dc.addOrderedRenderable(tip);
  }
  public void lookAt(Position lookAtPos, long timeToMove) {
    BasicFlyView view = (BasicFlyView) this.getView();
    Vec4 lookDirection;
    double distanceToSurface;
    Vec4 currentLookAtPt = view.getCenterPoint();
    Position newPosition;
    if (currentLookAtPt == null) {
      view.getGlobe().computePointFromPosition(lookAtPos);
      double elevAtLookAtPos =
          view.getGlobe().getElevation(lookAtPos.getLatitude(), lookAtPos.getLongitude());
      newPosition = new Position(lookAtPos, elevAtLookAtPos + 10000);
    } else {
      Vec4 currentEyePt = view.getEyePoint();
      distanceToSurface = currentEyePt.distanceTo3(currentLookAtPt);
      lookDirection = currentLookAtPt.subtract3(currentEyePt).normalize3();
      Vec4 newLookAtPt = view.getGlobe().computePointFromPosition(lookAtPos);
      Vec4 flyToPoint = newLookAtPt.add3(lookDirection.multiply3(-distanceToSurface));
      newPosition = view.getGlobe().computePositionFromPoint(flyToPoint);
    }

    ViewUtil.ViewState viewCoords = view.getViewState(newPosition, lookAtPos);

    FlyToFlyViewAnimator panAnimator =
        FlyToFlyViewAnimator.createFlyToFlyViewAnimator(
            view,
            view.getEyePosition(),
            newPosition,
            view.getHeading(),
            viewCoords.getHeading(),
            view.getPitch(),
            viewCoords.getPitch(),
            view.getEyePosition().getElevation(),
            viewCoords.getPosition().getElevation(),
            timeToMove,
            WorldWind.ABSOLUTE);

    this.gotoAnimControl.put(VIEW_ANIM_PAN, panAnimator);
    this.getView().firePropertyChange(AVKey.VIEW, null, this.getView());

    view.firePropertyChange(AVKey.VIEW, null, view);
  }
  /**
   * Compute points on either side of a line segment. This method requires a point on the line, and
   * either a next point, previous point, or both.
   *
   * @param point Center point about which to compute side points.
   * @param prev Previous point on the line. May be null if {@code next} is non-null.
   * @param next Next point on the line. May be null if {@code prev} is non-null.
   * @param leftPositions Left position will be added to this list.
   * @param rightPositions Right position will be added to this list.
   * @param halfWidth Distance from the center line to the left or right lines.
   * @param globe Current globe.
   */
  protected void generateParallelPoints(
      Vec4 point,
      Vec4 prev,
      Vec4 next,
      List<Position> leftPositions,
      List<Position> rightPositions,
      double halfWidth,
      Globe globe) {
    if ((point == null) || (prev == null && next == null)) {
      String message = Logging.getMessage("nullValue.PointIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }
    if (leftPositions == null || rightPositions == null) {
      String message = Logging.getMessage("nullValue.PositionListIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }
    if (globe == null) {
      String message = Logging.getMessage("nullValue.GlobeIsNull");
      Logging.logger().severe(message);
      throw new IllegalArgumentException(message);
    }

    Vec4 offset;
    Vec4 normal = globe.computeSurfaceNormalAtPoint(point);

    // Compute vector in the direction backward along the line.
    Vec4 backward = (prev != null) ? prev.subtract3(point) : point.subtract3(next);

    // Compute a vector perpendicular to segment BC, and the globe normal vector.
    Vec4 perpendicular = backward.cross3(normal);

    double length;
    // If both next and previous points are supplied then calculate the angle that bisects the angle
    // current, next, prev.
    if (next != null && prev != null && !Vec4.areColinear(prev, point, next)) {
      // Compute vector in the forward direction.
      Vec4 forward = next.subtract3(point);

      // Calculate the vector that bisects angle ABC.
      offset = forward.normalize3().add3(backward.normalize3());
      offset = offset.normalize3();

      // Compute the scalar triple product of the vector BC, the normal vector, and the offset
      // vector to
      // determine if the offset points to the left or the right of the control line.
      double tripleProduct = perpendicular.dot3(offset);
      if (tripleProduct < 0) {
        offset = offset.multiply3(-1);
      }

      // Determine the length of the offset vector that will keep the left and right lines parallel
      // to the control
      // line.
      Angle theta = backward.angleBetween3(offset);
      if (!Angle.ZERO.equals(theta)) length = halfWidth / theta.sin();
      else length = halfWidth;
    } else {
      offset = perpendicular.normalize3();
      length = halfWidth;
    }
    offset = offset.multiply3(length);

    // Determine the left and right points by applying the offset.
    Vec4 ptRight = point.add3(offset);
    Vec4 ptLeft = point.subtract3(offset);

    // Convert cartesian points to geographic.
    Position posLeft = globe.computePositionFromPoint(ptLeft);
    Position posRight = globe.computePositionFromPoint(ptRight);

    leftPositions.add(posLeft);
    rightPositions.add(posRight);
  }
  protected void onMoveTo(
      Position focalPosition,
      ViewInputAttributes.DeviceAttributes deviceAttributes,
      ViewInputAttributes.ActionAttributes actionAttribs) {
    BasicFlyView view = (BasicFlyView) this.getView();
    if (view == null) // include this test to ensure any derived implementation performs it
    {
      return;
    }

    // We're treating a speed parameter as smoothing here. A greater speed results in greater
    // smoothing and
    // slower response. Therefore the min speed used at lower altitudes ought to be *greater* than
    // the max
    // speed used at higher altitudes.
    double smoothing = this.getScaleValueElevation(deviceAttributes, actionAttribs);
    if (!actionAttribs.isEnableSmoothing()) smoothing = 0.0;

    Vec4 currentLookAtPt = view.getCenterPoint();
    if (currentLookAtPt == null) {
      currentLookAtPt = view.getGlobe().computePointFromPosition(focalPosition);
    }

    Vec4 currentEyePt = view.getEyePoint();
    double distanceToSurface = currentEyePt.distanceTo3(currentLookAtPt);
    Vec4 lookDirection = currentEyePt.subtract3(currentLookAtPt).normalize3();
    Vec4 newLookAtPt = view.getGlobe().computePointFromPosition(focalPosition);
    Vec4 flyToPoint = newLookAtPt.add3(lookDirection.multiply3(distanceToSurface));

    Position newPosition = view.getGlobe().computePositionFromPoint(flyToPoint);

    ViewUtil.ViewState viewCoords = view.getViewState(newPosition, focalPosition);

    this.stopAnimators();
    this.gotoAnimControl.put(
        VIEW_ANIM_HEADING,
        new RotateToAngleAnimator(
            view.getHeading(),
            viewCoords.getHeading(),
            smoothing,
            ViewPropertyAccessor.createHeadingAccessor(view)));
    this.gotoAnimControl.put(
        VIEW_ANIM_PITCH,
        new RotateToAngleAnimator(
            view.getPitch(),
            viewCoords.getPitch(),
            smoothing,
            ViewPropertyAccessor.createPitchAccessor(view)));

    double elevation =
        ((FlyViewLimits) view.getViewPropertyLimits())
            .limitEyeElevation(newPosition, view.getGlobe());
    if (elevation != newPosition.getElevation()) {
      newPosition = new Position(newPosition, elevation);
    }
    this.gotoAnimControl.put(
        VIEW_ANIM_POSITION,
        new MoveToPositionAnimator(
            view.getEyePosition(),
            newPosition,
            smoothing,
            ViewPropertyAccessor.createEyePositionAccessor(view)));

    view.firePropertyChange(AVKey.VIEW, null, view);
  }
  protected Vec4[] calculateBentNormals(int width, int height, Vec4[] verts, int padding) {
    int padding2 = padding * 2;

    if (verts.length != (width + padding2) * (height + padding2))
      throw new IllegalStateException("Illegal vertices length");

    Vec4[] norms = new Vec4[width * height];

    for (int y = 0; y < height; y++) {
      for (int x = 0; x < width; x++) {
        Vec4 vec =
            verts[getArrayIndex(width + padding2, height + padding2, x + padding, y + padding)];
        if (vec != null) {
          Vec4 vecnorm = vec.normalize3();
          Vec4[] maxes = new Vec4[16];
          double[] angles = new double[16];
          for (int i = 0; i < angles.length; i++) {
            angles[i] = Double.MAX_VALUE;
          }

          //  2  3  4  5  6
          //  1           7
          //  0           8
          // 15           9
          // 14 13 12 11 10

          // for (int i = 1; i <= padding; i++)
          for (int i = 2; i <= padding; i += 2) {
            Vec4[] vecs = new Vec4[16];
            vecs[0] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding - i, y + padding)];
            vecs[2] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding - i, y + padding - i)];
            vecs[4] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding, y + padding - i)];
            vecs[6] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding + i, y + padding - i)];
            vecs[8] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding + i, y + padding)];
            vecs[10] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding + i, y + padding + i)];
            vecs[12] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding, y + padding + i)];
            vecs[14] =
                verts[
                    getArrayIndex(
                        width + padding2, height + padding2, x + padding - i, y + padding + i)];
            if (i % 2 == 0) {
              int i2 = i / 2;
              vecs[1] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding - i, y + padding - i2)];
              vecs[3] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding - i2, y + padding - i)];
              vecs[5] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding + i2, y + padding - i)];
              vecs[7] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding + i, y + padding - i2)];
              vecs[9] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding + i, y + padding + i2)];
              vecs[11] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding + i2, y + padding + i)];
              vecs[13] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding - i2, y + padding + i)];
              vecs[15] =
                  verts[
                      getArrayIndex(
                          width + padding2, height + padding2, x + padding - i, y + padding + i2)];
            }

            for (int j = 0; j < maxes.length; j++) {
              if (vecs[j] != null) {
                Vec4 v = vecs[j].subtract3(vec).normalize3();
                double angle = Math.acos(v.dot3(vecnorm));
                if (angle < angles[j]) {
                  angles[j] = angle;
                  maxes[j] = v;
                }
              }
            }
          }

          Vec4 normal = Vec4.ZERO;
          for (int i = 0; i < maxes.length; i++) {
            if (maxes[i] != null) {
              Vec4 n = maxes[i].cross3(vecnorm).cross3(maxes[i]);
              normal = normal.add3(n);
            }
          }
          if (normal != Vec4.ZERO) {
            norms[getArrayIndex(width, height, x, y)] = normal.normalize3();
          }
        }
      }
    }

    return norms;
  }
Esempio n. 12
0
  // Rendering
  public void draw(DrawContext dc) {
    GL gl = dc.getGL();

    boolean attribsPushed = false;
    boolean modelviewPushed = false;
    boolean projectionPushed = false;

    try {
      gl.glPushAttrib(
          GL.GL_DEPTH_BUFFER_BIT
              | GL.GL_COLOR_BUFFER_BIT
              | GL.GL_ENABLE_BIT
              | GL.GL_TEXTURE_BIT
              | GL.GL_TRANSFORM_BIT
              | GL.GL_VIEWPORT_BIT
              | GL.GL_CURRENT_BIT);
      attribsPushed = true;

      gl.glDisable(GL.GL_TEXTURE_2D); // no textures

      gl.glEnable(GL.GL_BLEND);
      gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
      gl.glDisable(GL.GL_DEPTH_TEST);

      double width = this.size.width;
      double height = this.size.height;

      // Load a parallel projection with xy dimensions (viewportWidth, viewportHeight)
      // into the GL projection matrix.
      java.awt.Rectangle viewport = dc.getView().getViewport();
      gl.glMatrixMode(javax.media.opengl.GL.GL_PROJECTION);
      gl.glPushMatrix();
      projectionPushed = true;
      gl.glLoadIdentity();
      double maxwh = width > height ? width : height;
      gl.glOrtho(0d, viewport.width, 0d, viewport.height, -0.6 * maxwh, 0.6 * maxwh);

      gl.glMatrixMode(GL.GL_MODELVIEW);
      gl.glPushMatrix();
      modelviewPushed = true;
      gl.glLoadIdentity();

      // Scale to a width x height space
      // located at the proper position on screen
      double scale = this.computeScale(viewport);
      Vec4 locationSW = this.computeLocation(viewport, scale);
      gl.glTranslated(locationSW.x(), locationSW.y(), locationSW.z());
      gl.glScaled(scale, scale, 1);

      // Compute scale size in real world
      Position referencePosition = dc.getViewportCenterPosition();
      if (referencePosition != null) {
        Vec4 groundTarget = dc.getGlobe().computePointFromPosition(referencePosition);
        Double distance = dc.getView().getEyePoint().distanceTo3(groundTarget);
        this.pixelSize = dc.getView().computePixelSizeAtDistance(distance);
        Double scaleSize = this.pixelSize * width * scale; // meter
        String unitLabel = "m";
        if (this.unit.equals(UNIT_METRIC)) {
          if (scaleSize > 10000) {
            scaleSize /= 1000;
            unitLabel = "Km";
          }
        } else if (this.unit.equals(UNIT_IMPERIAL)) {
          scaleSize *= 3.280839895; // feet
          unitLabel = "ft";
          if (scaleSize > 5280) {
            scaleSize /= 5280;
            unitLabel = "mile(s)";
          }
        }

        // Rounded division size
        int pot = (int) Math.floor(Math.log10(scaleSize));
        if (!Double.isNaN(pot)) {
          int digit = Integer.parseInt(String.format("%.0f", scaleSize).substring(0, 1));
          double divSize = digit * Math.pow(10, pot);
          if (digit >= 5) divSize = 5 * Math.pow(10, pot);
          else if (digit >= 2) divSize = 2 * Math.pow(10, pot);
          double divWidth = width * divSize / scaleSize;

          // Draw scale
          if (!dc.isPickingMode()) {
            // Set color using current layer opacity
            Color backColor = this.getBackgroundColor(this.color);
            float[] colorRGB = backColor.getRGBColorComponents(null);
            gl.glColor4d(
                colorRGB[0],
                colorRGB[1],
                colorRGB[2],
                (double) backColor.getAlpha() / 255d * this.getOpacity());
            gl.glTranslated((width - divWidth) / 2, 0d, 0d);
            this.drawScale(dc, divWidth, height);

            colorRGB = this.color.getRGBColorComponents(null);
            gl.glColor4d(colorRGB[0], colorRGB[1], colorRGB[2], this.getOpacity());
            gl.glTranslated(-1d / scale, 1d / scale, 0d);
            this.drawScale(dc, divWidth, height);

            // Draw label
            String label = String.format("%.0f ", divSize) + unitLabel;
            gl.glLoadIdentity();
            gl.glDisable(GL.GL_CULL_FACE);
            drawLabel(
                dc,
                label,
                locationSW.add3(
                    new Vec4(divWidth * scale / 2 + (width - divWidth) / 2, height * scale, 0)));
          } else {
            // Picking
            this.pickSupport.clearPickList();
            this.pickSupport.beginPicking(dc);
            // Draw unique color across the map
            Color color = dc.getUniquePickColor();
            int colorCode = color.getRGB();
            // Add our object(s) to the pickable list
            this.pickSupport.addPickableObject(colorCode, this, referencePosition, false);
            gl.glColor3ub((byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue());
            gl.glTranslated((width - divWidth) / 2, 0d, 0d);
            this.drawRectangle(dc, divWidth, height);
            // Done picking
            this.pickSupport.endPicking(dc);
            this.pickSupport.resolvePick(dc, dc.getPickPoint(), this);
          }
        }
      }
    } finally {
      if (projectionPushed) {
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glPopMatrix();
      }
      if (modelviewPushed) {
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glPopMatrix();
      }
      if (attribsPushed) gl.glPopAttrib();
    }
  }