Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / awt / AWTInputHandler.java
blobd41256ce65f4f5ba92cc1332cee7121e4c3e073c
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.awt;
9 import gov.nasa.worldwind.*;
10 import gov.nasa.worldwind.geom.*;
12 import javax.swing.event.*;
13 import java.awt.event.*;
14 import java.util.*;
15 import java.util.logging.Level;
17 /**
18 * @author tag
19 * @version $Id: AWTInputHandler.java 1820 2007-05-10 16:05:29Z dcollins $
21 @SuppressWarnings({"UnnecessaryReturnStatement"})
22 public class AWTInputHandler extends AVListImpl
23 implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener, InputHandler
25 private WorldWindow worldWindow = null;
26 private final EventListenerList eventListeners = new EventListenerList();
27 private Position previousPickPosition = null;
28 private PickedObjectList lastPickedObjects = null;
29 private PickedObjectList hoverObjects;
30 private boolean isHovering = false;
31 private javax.swing.Timer hoverTimer = new javax.swing.Timer(600, new ActionListener()
33 public void actionPerformed(ActionEvent actionEvent)
35 if (AWTInputHandler.this.hoverMatches())
37 AWTInputHandler.this.isHovering = true;
38 AWTInputHandler.this.callSelectListeners(new SelectEvent(AWTInputHandler.this.worldWindow,
39 SelectEvent.HOVER, null, AWTInputHandler.this.hoverObjects));
40 AWTInputHandler.this.hoverTimer.stop();
43 });
44 // Current AWT mouse state.
45 private java.awt.Point lastMousePoint = new java.awt.Point();
46 // private volatile int mouseClickButton;
47 // private volatile int mouseClickCount;
48 // private final int mouseClickCoalesceTime = 350;
49 // Repeating AWT key timer. Used to simulate keyboard polling.
50 private final Integer[] POLLED_KEYS =
52 KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT,
53 KeyEvent.VK_UP, KeyEvent.VK_DOWN, KeyEvent.VK_PAGE_UP,
54 KeyEvent.VK_PAGE_DOWN, KeyEvent.VK_ADD, KeyEvent.VK_EQUALS,
55 KeyEvent.VK_SUBTRACT, KeyEvent.VK_MINUS
57 private KeyPollTimer keyPollTimer = new KeyPollTimer(25, Arrays.asList(POLLED_KEYS),
58 new ActionListener()
60 public void actionPerformed(ActionEvent actionEvent)
62 if (actionEvent == null)
63 return;
64 Object source = actionEvent.getSource();
65 if (source != null && source instanceof Integer)
66 AWTInputHandler.this.keysPolled((Integer) source, actionEvent.getModifiers());
68 });
69 // Value interpolation timer. Smooths responses to certain input events.
70 private InterpolatorTimer interpolatorTimer = new InterpolatorTimer(15);
71 private java.beans.PropertyChangeListener viewPropertyListener = null;
72 private InterpolatorTimer.ViewProperties viewTarget = null;
73 private boolean smoothViewChange = true;
74 // View LatLon properties.
75 private final double viewLatLonMinChangeFactor = 0.00001;
76 private final double viewLatLonMaxChangeFactor = 0.2;
77 private final double viewLatLonErrorThresold = 0.000000001;
78 private final double viewLatLonStepCoefficient = 0.6;
79 private final double viewLatLonCenterStepCoefficient = 0.1;
80 // View heading and pitch (angle) properties.
81 private final double viewAngleChangeFactor = 0.25;
82 private final double viewAngleErrorThreshold = 0.0001;
83 private final double viewAngleStepCoefficient = 0.3;
84 private final double viewAngleResetStepCoefficient = 0.1;
85 // View zoom properties.
86 private final double viewZoomChangeFactor = 0.05;
87 private final double viewZoomErrorThreshold = 0.001;
88 private final double viewZoomStepCoefficient = 0.1;
90 public void setEventSource(WorldWindow newWorldWindow)
92 if (newWorldWindow != null && !(newWorldWindow instanceof java.awt.Component))
94 String message = WorldWind.retrieveErrMsg("awt.AWTInputHandler.EventSourceNotAComponent");
95 WorldWind.logger().log(Level.FINER, message);
96 throw new IllegalArgumentException(message);
99 if (newWorldWindow == this.worldWindow)
100 return;
102 if (this.worldWindow != null)
104 java.awt.Component c = (java.awt.Component) this.worldWindow;
105 c.removeKeyListener(this);
106 c.removeMouseMotionListener(this);
107 c.removeMouseListener(this);
108 c.removeMouseWheelListener(this);
110 if (this.keyPollTimer != null)
111 c.removeKeyListener(this.keyPollTimer);
114 this.worldWindow = newWorldWindow;
116 if (this.worldWindow == null)
117 return;
119 java.awt.Component c = (java.awt.Component) this.worldWindow;
120 c.addKeyListener(this);
121 c.addMouseMotionListener(this);
122 c.addMouseListener(this);
123 c.addMouseWheelListener(this);
125 if (this.keyPollTimer != null)
126 c.addKeyListener(this.keyPollTimer);
129 public WorldWindow getEventSource()
131 return this.worldWindow;
134 public void setHoverDelay(int delay)
136 this.hoverTimer.setDelay(delay);
139 public int getHoverDelay()
141 return this.hoverTimer.getDelay();
144 public void keyTyped(KeyEvent keyEvent)
146 if (this.worldWindow == null) // include this test to ensure any derived implementation performs it
147 return;
150 public void keyPressed(KeyEvent keyEvent)
152 if (this.worldWindow == null) // include this test to ensure any derived implementation performs it
153 return;
156 public void keyReleased(KeyEvent keyEvent)
158 if (this.worldWindow == null)
159 return;
161 if (keyEvent == null)
162 return;
164 View view = this.worldWindow.getView();
165 if (view == null)
166 return;
168 int keyCode = keyEvent.getKeyCode();
169 if (keyCode == KeyEvent.VK_SPACE)
171 this.interpolatorTimer.stop();
173 else if (keyCode == KeyEvent.VK_N)
175 InterpolatorTimer.ViewProperties newProps = new InterpolatorTimer.ViewProperties();
176 newProps.heading = Angle.fromDegrees(0);
177 this.setViewProperties(view, newProps, this.viewAngleResetStepCoefficient, this.viewAngleErrorThreshold,
178 true);
180 else if (keyCode == KeyEvent.VK_R)
182 InterpolatorTimer.ViewProperties newProps = new InterpolatorTimer.ViewProperties();
183 newProps.heading = Angle.fromDegrees(0);
184 newProps.pitch = Angle.fromDegrees(0);
185 this.setViewProperties(view, newProps, this.viewAngleResetStepCoefficient, this.viewAngleErrorThreshold,
186 true);
190 public void keysPolled(int keyCode, int modifiers)
192 if (this.worldWindow == null)
193 return;
195 View view = this.worldWindow.getView();
196 if (view == null)
197 return;
199 int slowMask = (modifiers & InputEvent.ALT_DOWN_MASK);
200 boolean slow = slowMask != 0x0;
202 if (areModifiersExactly(modifiers, slowMask))
204 double sinHeading = view.getHeading().sin();
205 double cosHeading = view.getHeading().cos();
206 double latFactor = 0;
207 double lonFactor = 0;
208 if (keyCode == KeyEvent.VK_LEFT)
210 latFactor = sinHeading;
211 lonFactor = -cosHeading;
213 else if (keyCode == KeyEvent.VK_RIGHT)
215 latFactor = -sinHeading;
216 lonFactor = cosHeading;
218 else if (keyCode == KeyEvent.VK_UP)
220 latFactor = cosHeading;
221 lonFactor = sinHeading;
223 else if (keyCode == KeyEvent.VK_DOWN)
225 latFactor = -cosHeading;
226 lonFactor = -sinHeading;
228 if (latFactor != 0 || lonFactor != 0)
230 Globe globe = this.worldWindow.getModel().getGlobe();
231 if (globe != null)
233 LatLon latLonChange = this.computeViewLatLonChange(view, globe, 10 * latFactor, 10 * lonFactor,
234 slow);
235 this.setViewLatLon(view, this.computeNewViewLatLon(view, latLonChange.getLatitude(),
236 latLonChange.getLongitude()));
237 return;
242 double headingFactor = 0;
243 double pitchFactor = 0;
244 if (areModifiersExactly(modifiers, slowMask))
246 if (keyCode == KeyEvent.VK_PAGE_DOWN)
247 pitchFactor = 1;
248 else if (keyCode == KeyEvent.VK_PAGE_UP)
249 pitchFactor = -1;
251 else if (areModifiersExactly(modifiers, InputEvent.SHIFT_DOWN_MASK | slowMask))
253 if (keyCode == KeyEvent.VK_LEFT)
254 headingFactor = -1;
255 else if (keyCode == KeyEvent.VK_RIGHT)
256 headingFactor = 1;
257 else if (keyCode == KeyEvent.VK_UP)
258 pitchFactor = -1;
259 else if (keyCode == KeyEvent.VK_DOWN)
260 pitchFactor = 1;
262 if (headingFactor != 0)
264 this.setViewAngle(view, this.computeNewViewHeading(view,
265 this.computeViewAngleChange(4 * headingFactor, slow)), null);
266 return;
268 else if (pitchFactor != 0)
270 this.setViewAngle(view, null, this.computeNewViewPitch(view,
271 this.computeViewAngleChange(4 * pitchFactor, slow)));
272 return;
275 double zoomFactor = 0;
276 if (areModifiersExactly(modifiers, slowMask))
278 if (keyCode == KeyEvent.VK_ADD ||
279 keyCode == KeyEvent.VK_EQUALS)
280 zoomFactor = -1;
281 else if (keyCode == KeyEvent.VK_SUBTRACT ||
282 keyCode == KeyEvent.VK_MINUS)
283 zoomFactor = 1;
285 else if (areModifiersExactly(modifiers, InputEvent.CTRL_DOWN_MASK | slowMask)
286 || areModifiersExactly(modifiers, InputEvent.META_DOWN_MASK | slowMask))
288 if (keyCode == KeyEvent.VK_UP)
289 zoomFactor = -1;
290 else if (keyCode == KeyEvent.VK_DOWN)
291 zoomFactor = 1;
293 if (zoomFactor != 0)
295 this.setViewZoom(view, this.computeNewViewZoom(view, this.computeZoomViewChange(zoomFactor, slow)));
296 return;
300 public void mouseClicked(final MouseEvent mouseEvent)
302 if (this.worldWindow == null) // include this test to ensure any subsequent implementation performs it
303 return;
305 if (mouseEvent == null)
306 return;
308 View view = this.worldWindow.getView();
309 if (view == null)
310 return;
312 // int button = mouseEvent.getButton();
313 // if (this.mouseClickButton != button)
314 // this.mouseClickCount = 1;
315 // else
316 // this.mouseClickCount = mouseEvent.getClickCount();
317 // this.mouseClickButton = button;
318 // if (this.mouseClickCount == 1)
319 // {
320 // javax.swing.Timer clickTimer = new javax.swing.Timer(this.mouseClickCoalesceTime, new ActionListener()
321 // {
322 // public void actionPerformed(ActionEvent evt)
323 // {
324 // AWTInputHandler.this.mouseClickedCoalesced(mouseEvent, AWTInputHandler.this.mouseClickCount);
325 // }
326 // });
327 // clickTimer.setRepeats(false);
328 // clickTimer.start();
329 // }
331 // if ( (null != this.lastPickedObjects)
332 // && (null != this.lastPickedObjects.getTopObject())
333 // && this.lastPickedObjects.getTopObject().isTerrain())
334 // return;
336 if (this.lastPickedObjects != null && this.lastPickedObjects.size() > 0)
338 PickedObject top = this.lastPickedObjects.getTopObject();
339 if (top != null && top.isTerrain())
341 InterpolatorTimer.ViewProperties newProps = new InterpolatorTimer.ViewProperties();
342 Position position = top.getPosition();
343 newProps.latLon = new LatLon(position.getLatitude(), position.getLongitude());
344 this.setViewProperties(view, newProps, this.viewLatLonCenterStepCoefficient,
345 this.viewLatLonErrorThresold, true);
346 return;
349 // Something is under the cursor, so it's deemed "selected".
351 if (MouseEvent.BUTTON1 == mouseEvent.getButton())
353 if (mouseEvent.getClickCount() % 2 == 1)
355 this.callSelectListeners(new SelectEvent(this.worldWindow, SelectEvent.LEFT_CLICK,
356 mouseEvent, this.lastPickedObjects));
358 else
360 this.callSelectListeners(new SelectEvent(this.worldWindow, SelectEvent.LEFT_DOUBLE_CLICK,
361 mouseEvent, this.lastPickedObjects));
364 else if (MouseEvent.BUTTON3 == mouseEvent.getButton())
366 this.callSelectListeners(new SelectEvent(this.worldWindow, SelectEvent.RIGHT_CLICK,
367 mouseEvent, this.lastPickedObjects));
370 view.firePropertyChange(AVKey.VIEW, null, view);
374 public void mousePressed(MouseEvent mouseEvent)
376 if (this.worldWindow == null)
377 return;
379 this.cancelHover();
382 public void mouseReleased(MouseEvent mouseEvent)
384 if (this.worldWindow == null)
385 return;
387 this.doHover(true);
390 public void mouseEntered(MouseEvent mouseEvent)
392 if (this.worldWindow == null)
393 return;
395 this.cancelHover();
398 public void mouseExited(MouseEvent mouseEvent)
400 if (this.worldWindow == null)
401 return;
403 this.cancelHover();
406 public void mouseDragged(MouseEvent mouseEvent)
408 if (this.worldWindow == null)
409 return;
411 if (mouseEvent == null)
412 return;
414 View view = this.worldWindow.getView();
415 if (view == null)
416 return;
418 if (this.worldWindow.getModel() == null)
419 return;
421 java.awt.Point mouseMove = new java.awt.Point(mouseEvent.getPoint().x - this.lastMousePoint.x,
422 mouseEvent.getPoint().y - this.lastMousePoint.y);
424 if (areModifiersExactly(mouseEvent, InputEvent.BUTTON1_DOWN_MASK))
426 LatLon latLonChange = null;
428 Position prev = view.computePositionFromScreenPoint(this.lastMousePoint.x, this.lastMousePoint.y);
429 Position cur = view.computePositionFromScreenPoint(mouseEvent.getPoint().x, mouseEvent.getPoint().y);
430 if (prev != null && cur != null)
432 latLonChange = new LatLon(prev.getLatitude().subtract(cur.getLatitude()),
433 prev.getLongitude().subtract(cur.getLongitude()));
435 else
437 Globe globe = this.worldWindow.getModel().getGlobe();
438 if (globe != null)
440 double sinHeading = view.getHeading().sin();
441 double cosHeading = view.getHeading().cos();
442 double latFactor = cosHeading * mouseMove.y + sinHeading * mouseMove.x;
443 double lonFactor = sinHeading * mouseMove.y - cosHeading * mouseMove.x;
444 latLonChange = this.computeViewLatLonChange(view, globe, latFactor, lonFactor, false);
448 if (latLonChange != null)
450 this.setViewLatLon(view, this.computeNewViewLatLon(view, latLonChange.getLatitude(),
451 latLonChange.getLongitude()));
454 else if (areModifiersExactly(mouseEvent, InputEvent.BUTTON3_DOWN_MASK)
455 || areModifiersExactly(mouseEvent, InputEvent.BUTTON1_DOWN_MASK | InputEvent.CTRL_DOWN_MASK))
457 double headingDirection = 1;
458 Object source = mouseEvent.getSource();
459 if (source != null && source instanceof java.awt.Component)
461 java.awt.Component component = (java.awt.Component) source;
462 if (mouseEvent.getPoint().y < component.getHeight() / 2)
463 headingDirection = -1;
465 this.setViewAngle(view,
466 this.computeNewViewHeading(view, this.computeViewAngleChange(headingDirection * mouseMove.x, false)),
467 this.computeNewViewPitch(view, this.computeViewAngleChange(mouseMove.y, false)));
470 this.lastMousePoint = mouseEvent.getPoint();
473 public void mouseMoved(MouseEvent mouseEvent)
475 if (this.worldWindow == null)
476 return;
478 if (mouseEvent == null)
479 return;
481 this.lastMousePoint = mouseEvent.getPoint();
483 View view = this.worldWindow.getView();
484 if (view == null)
485 return;
487 // Forward event to mouseDragged() for OS X.
488 // if (areModifiersExactly(mouseEvent, InputEvent.CTRL_DOWN_MASK))
489 // {
490 // this.mouseDragged(mouseEvent);
491 // }
493 Model model = this.worldWindow.getModel();
494 if (model == null)
495 return;
497 Globe globe = model.getGlobe();
498 if (globe == null)
499 return;
501 this.lastPickedObjects = this.worldWindow.pick(mouseEvent.getPoint());
502 PickedObject top = null;
503 if (this.lastPickedObjects != null && this.lastPickedObjects.size() > 0)
504 top = this.lastPickedObjects.getTopObject();
506 PickedObjectList selected = null;
507 if (!(top == null || top.isTerrain())) // if not terrain
508 selected = this.lastPickedObjects;
510 this.callSelectListeners(new SelectEvent(this.worldWindow, SelectEvent.ROLLOVER, mouseEvent, selected));
512 this.doHover(true);
514 Position p = null;
515 if (null != top && top.hasPosition())
516 p = top.getPosition();
517 else if (this.lastPickedObjects != null && this.lastPickedObjects.getTerrainObject() != null)
518 p = this.lastPickedObjects.getTerrainObject().getPosition();
520 this.callPositionListeners(new PositionEvent(this.worldWindow, mouseEvent, this.previousPickPosition, p));
521 this.previousPickPosition = p;
524 public void mouseWheelMoved(MouseWheelEvent mouseWheelEvent)
526 if (this.worldWindow == null)
527 return;
529 if (mouseWheelEvent == null)
530 return;
532 View view = this.worldWindow.getView();
533 if (view == null)
534 return;
536 int wheelRotation = mouseWheelEvent.getWheelRotation();
537 double wheelDirection = Math.signum(wheelRotation);
538 this.setViewZoom(view, this.computeNewViewZoom(view, this.computeZoomViewChange(wheelDirection, false)));
541 private static boolean areModifiersExactly(InputEvent inputEvent, int mask)
543 return areModifiersExactly(inputEvent.getModifiersEx(), mask);
546 private static boolean areModifiersExactly(int modifiersEx, int mask)
548 return modifiersEx == mask;
551 private boolean isPickListEmpty(PickedObjectList pickList)
553 return pickList == null || pickList.size() < 1;
556 private void doHover(boolean reset)
558 if (!(this.isPickListEmpty(this.hoverObjects) || this.isPickListEmpty(this.lastPickedObjects)))
560 PickedObject hover = this.hoverObjects.getTopObject();
561 PickedObject last = this.lastPickedObjects.getTopObject();
563 if (hover != null && last != null && hover.getObject().equals(last.getObject()))
565 return; // object picked is the hover object. don't do anything but wait for the timer to expire.
569 this.cancelHover();
571 if (!reset)
572 return;
574 if ((null != this.lastPickedObjects)
575 && (null != this.lastPickedObjects.getTopObject())
576 && this.lastPickedObjects.getTopObject().isTerrain())
577 return;
579 this.hoverObjects = this.lastPickedObjects;
580 this.hoverTimer.restart();
583 private void cancelHover()
585 if (this.isHovering)
587 this.callSelectListeners(new SelectEvent(this.worldWindow, SelectEvent.HOVER, null, null));
590 this.isHovering = false;
591 this.hoverObjects = null;
592 this.hoverTimer.stop();
595 private boolean hoverMatches()
597 if (this.isPickListEmpty(this.lastPickedObjects) || this.isPickListEmpty(this.hoverObjects))
598 return false;
600 PickedObject lastTop = this.lastPickedObjects.getTopObject();
602 if (null != lastTop && lastTop.isTerrain())
603 return false;
605 PickedObject newTop = this.hoverObjects.getTopObject();
606 //noinspection SimplifiableIfStatement
607 if (lastTop == null || newTop == null || lastTop.getObject() == null || newTop.getObject() == null)
608 return false;
610 return lastTop.getObject().equals(newTop.getObject());
613 public void addSelectListener(SelectListener listener)
615 this.eventListeners.add(SelectListener.class, listener);
618 public void removeSelectListener(SelectListener listener)
620 this.eventListeners.remove(SelectListener.class, listener);
623 public void addPositionListener(PositionListener listener)
625 this.eventListeners.add(PositionListener.class, listener);
628 public void removePositionListener(PositionListener listener)
630 this.eventListeners.remove(PositionListener.class, listener);
633 private void callSelectListeners(SelectEvent event)
635 for (SelectListener listener : this.eventListeners.getListeners(SelectListener.class))
637 listener.selected(event);
641 private void callPositionListeners(PositionEvent event)
643 for (PositionListener listener : this.eventListeners.getListeners(PositionListener.class))
645 listener.moved(event);
649 private void setViewProperties(final View view, InterpolatorTimer.ViewProperties newProperties,
650 double stepCoefficient, double errorThreshold, boolean forceSmooth)
652 if (view == null)
654 String message = WorldWind.retrieveErrMsg("nullValue.ViewIsNull");
655 WorldWind.logger().log(Level.FINE, message);
656 throw new IllegalArgumentException(message);
658 if (newProperties == null)
660 String message = WorldWind.retrieveErrMsg("awt.InterpolatorTimer.ViewPropertiesIsNull");
661 WorldWind.logger().log(Level.FINE, message);
662 throw new IllegalArgumentException(message);
665 if (forceSmooth || this.smoothViewChange)
667 if (this.viewPropertyListener == null)
669 this.viewPropertyListener = new java.beans.PropertyChangeListener()
671 public void propertyChange(java.beans.PropertyChangeEvent evt)
673 Object newValue = evt.getNewValue();
674 if (newValue == null)
676 AWTInputHandler.this.viewTarget = null;
678 else if (newValue instanceof InterpolatorTimer.ViewProperties)
680 InterpolatorTimer.ViewProperties viewProps = (InterpolatorTimer.ViewProperties) newValue;
681 if (viewProps.latLon != null)
682 view.goToLatLon(viewProps.latLon);
683 if (viewProps.heading != null)
684 view.setHeading(viewProps.heading);
685 if (viewProps.pitch != null)
686 view.setPitch(viewProps.pitch);
687 if (viewProps.zoom != null)
688 view.setZoom(viewProps.zoom);
689 view.firePropertyChange(AVKey.VIEW, null, view);
694 InterpolatorTimer.ViewProperties begin = new InterpolatorTimer.ViewProperties();
695 if (newProperties.latLon != null)
696 begin.latLon = new LatLon(view.getPosition().getLatitude(), view.getPosition().getLongitude());
697 if (newProperties.heading != null)
698 begin.heading = view.getHeading();
699 if (newProperties.pitch != null)
700 begin.pitch = view.getPitch();
701 if (newProperties.zoom != null)
702 begin.zoom = view.getZoom();
703 this.viewTarget = newProperties;
704 this.interpolatorTimer.start(stepCoefficient, errorThreshold, begin, newProperties,
705 this.viewPropertyListener);
707 else
709 this.viewTarget = null;
710 this.interpolatorTimer.stop();
711 if (newProperties.latLon != null)
712 view.goToLatLon(newProperties.latLon);
713 if (newProperties.heading != null)
714 view.setHeading(newProperties.heading);
715 if (newProperties.pitch != null)
716 view.setPitch(newProperties.pitch);
717 if (newProperties.zoom != null)
718 view.setZoom(newProperties.zoom);
719 view.firePropertyChange(AVKey.VIEW, null, view);
723 private void setViewLatLon(final View view, LatLon newLatLon)
725 if (newLatLon == null)
727 String message = WorldWind.retrieveErrMsg("nullValue.LatLonIsNull");
728 WorldWind.logger().log(Level.FINE, message);
729 throw new IllegalArgumentException(message);
731 this.viewTarget = new InterpolatorTimer.ViewProperties();
732 this.viewTarget.latLon = newLatLon;
733 this.setViewProperties(view, this.viewTarget, this.viewLatLonStepCoefficient, this.viewLatLonErrorThresold,
734 false);
737 private LatLon computeNewViewLatLon(View view, Angle latChange, Angle lonChange)
739 double latDegrees;
740 double lonDegrees;
741 if (this.viewTarget != null && this.viewTarget.latLon != null)
743 latDegrees = this.viewTarget.latLon.getLatitude().getDegrees();
744 lonDegrees = this.viewTarget.latLon.getLongitude().getDegrees();
746 else
748 latDegrees = view.getPosition().getLatitude().getDegrees();
749 lonDegrees = view.getPosition().getLongitude().getDegrees();
751 latDegrees = latDegrees + latChange.getDegrees();
752 lonDegrees = lonDegrees + lonChange.getDegrees();
753 if (latDegrees < -90)
754 latDegrees = -90;
755 else if (latDegrees > 90)
756 latDegrees = 90;
757 if (lonDegrees < -180)
758 lonDegrees = lonDegrees + 360;
759 else if (lonDegrees > 180)
760 lonDegrees = lonDegrees - 360;
761 return LatLon.fromDegrees(latDegrees, lonDegrees);
764 private LatLon computeViewLatLonChange(View view, Globe globe, double latFactor, double lonFactor, boolean slow)
766 Point eye = view.getEyePoint();
767 if (eye == null)
768 return null;
770 double normAlt = clamp((eye.length() / globe.getMaximumRadius()) - 1, 0, 1);
771 double factor = ((1 - normAlt) * this.viewLatLonMinChangeFactor + normAlt * this.viewLatLonMaxChangeFactor)
772 * (slow ? 2.5e-1 : 1);
773 return LatLon.fromDegrees(latFactor * factor, lonFactor * factor);
776 private void setViewAngle(final View view, Angle newHeading, Angle newPitch)
778 if (newHeading == null && newPitch == null)
780 String message = WorldWind.retrieveErrMsg("nullValue.AngleIsNull");
781 WorldWind.logger().log(Level.FINE, message);
782 throw new IllegalArgumentException(message);
784 this.viewTarget = new InterpolatorTimer.ViewProperties();
785 this.viewTarget.heading = newHeading;
786 this.viewTarget.pitch = newPitch;
787 this.setViewProperties(view, this.viewTarget, this.viewAngleStepCoefficient, this.viewAngleErrorThreshold,
788 false);
791 private Angle computeNewViewHeading(View view, Angle change)
793 double degrees;
794 if (this.viewTarget != null && this.viewTarget.heading != null)
795 degrees = this.viewTarget.heading.getDegrees();
796 else
797 degrees = view.getHeading().getDegrees();
798 degrees = degrees + change.getDegrees();
799 if (degrees < 0)
800 degrees = degrees + 360;
801 else if (degrees > 360)
802 degrees = degrees - 360;
803 return Angle.fromDegrees(degrees);
806 private Angle computeViewAngleChange(double factor, boolean slow)
808 return Angle.fromDegrees(factor * this.viewAngleChangeFactor * (slow ? 2.5e-1 : 1));
811 private Angle computeNewViewPitch(View view, Angle change)
813 double degrees;
814 if (this.viewTarget != null && this.viewTarget.pitch != null)
815 degrees = this.viewTarget.pitch.getDegrees();
816 else
817 degrees = view.getPitch().getDegrees();
818 degrees = degrees + change.getDegrees();
819 Angle[] constraints = view.getPitchConstraints();
820 return Angle.fromDegrees(clamp(degrees, constraints[0].getDegrees(), constraints[1].getDegrees()));
823 private void setViewZoom(final View view, double newZoom)
825 this.viewTarget = new InterpolatorTimer.ViewProperties();
826 this.viewTarget.zoom = newZoom;
827 this.setViewProperties(view, this.viewTarget, this.viewZoomStepCoefficient, this.viewZoomErrorThreshold,
828 false);
831 private double computeNewViewZoom(View view, double change)
833 double logZoom;
834 if (this.viewTarget != null && this.viewTarget.zoom != null)
835 logZoom = Math.log(this.viewTarget.zoom);
836 else
837 logZoom = Math.log(view.getZoom());
838 logZoom = logZoom + change;
839 double[] constraints = view.getZoomConstraints();
840 return clamp(Math.exp(logZoom), constraints[0], constraints[1]);
843 private double computeZoomViewChange(double factor, boolean slow)
845 return factor * this.viewZoomChangeFactor * (slow ? 2.5e-1 : 1);
848 private static double clamp(double x, double min, double max)
850 return x < min ? min : (x > max ? max : x);