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);
    }
  }
  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);
  }
  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);
  }