Select to view content in your preferred language

How to customize smooth PanTo & Zoom on mouse event ?

5343
5
Jump to solution
04-09-2013 01:13 AM
JeremieJoalland1
Occasional Contributor II
Following my previous post "How to disable/enable built-in navigation functions on JMap", I'm trying to implement an application which can switch between built-in and customized navigation functions.

Basically, in one mode I have the built-in navigation of ArcGIS Runtime :
- PanTo is very smooth and accurate, whatever the MouseDragged speed,
- ZoomIn & ZoomOut is also very smooth, on MouseWheel event, and the location under mouse pointer remain under the mouse.

Now I've implemented my customized MapOverlay, in order to have another mode where I block the built-in navigation by blocking the mouse events, and I've implemented my own navigation by overriding mouse events but with bad results :
- PanTo is not smooth at all and not accurate. If the MouseDragged is brief, result can be acceptable, but when you move the mouse slowly, it's like JMap component can't manage fast enough the panTo instructions, as map fire more mapExtentChanged() event. the result is that the map is not attached to the mouse pointer, like it is with the built-in panTo.
- ZoomIn & ZoomOut is very smooth like built-in, but the location under mouse pointer change so behavior is different from built-in.

I've also overrided all navigation functions of JMap component, just in order to check (print in console) when these functions are called, and the built-in navigation don't call panTo() or zoom(). Also during the mouseDragged, the mapExtentChange event occurs only one time once the mouse button is released...

Is it possible to implement a customized PanTo which has the same nice and smooth behavior than built-in ? Could you please help me on that ?

I've attached my sample application source code
0 Kudos
1 Solution

Accepted Solutions
JeremieJoalland1
Occasional Contributor II
I finally come with an acceptable panTo() function 😄
- First, the disable animation mode did a lot.
- Second, my code in onMouseDragged() was not good regarding the Point I calculated to panTo, and I corrected it.

in my customized MapEventsOverlay, I changed this :

private java.awt.Point  startPointOnScreen;

...

    @Override
    public void onMouseDragged(MouseEvent event) {
  // do not supper() to block the built-in navigation
     java.awt.Point pointScreen = event.getLocationOnScreen();
     Point endPointOnMap = this.getMap().toMapPoint(pointScreen.x, pointScreen.y);
     Point startPointOnMap = this.getMap().toMapPoint(startPointOnScreen.x, startPointOnScreen.y);
     
     double dx = endPointOnMap.getX() - startPointOnMap.getX();
     double dy = endPointOnMap.getY() - startPointOnMap.getY();
     
     // PAN TO new extent
     Point currentCenter = this.getMap().getExtent().getCenter();
     Point newCenter = new Point(currentCenter.getX() - dx, currentCenter.getY() - dy);
     
     this.getMap().panTo(newCenter);      
     
     this.startPointOnScreen = pointScreen;
    }

...

    @Override
    public void onMousePressed(MouseEvent event) {
  // do not supper() to block the built-in navigation
     this.startPointOnScreen = event.getLocationOnScreen();
    }

...

    @Override
    public void onMouseWheelMoved(MouseWheelEvent event) {
  // do not supper() to block the built-in navigation
  java.awt.Point pointScreen = MouseInfo.getPointerInfo().getLocation();
  // java.awt.Point pointScreen = event.getLocationOnScreen(); // => always return 0,0
  Point pointCenter = this.getMap().toMapPoint(pointScreen.x, pointScreen.y);
  
  // Turn ON animation - Add this to keep^a nice & smooth zoomIn/Out
  this.getMap().setAnimationMode(AnimationMode.ANIMATE);
  
  int notches = event.getWheelRotation();
  if (notches < 0) {
   // Zoom in
   this.getMap().zoom(1/factor, pointCenter);
  } else {
   // Zoom out
   this.getMap().zoom(factor, pointCenter);
  }
  
  // Turn OFF animation - Add this to keep^a nice & smooth zoomIn/Out
  this.getMap().setAnimationMode(AnimationMode.NONE);
    }


in my application code, I turn off/on the animation mode in action of my 2 buttons, depending if I want built-in panning or my customized panTo :

// Desactivate Built-in navigation
  btnCustomizedPanTo = new JButton();
  btnCustomizedPanTo.setText("ACTIVATE customized panTo");
  btnCustomizedPanTo.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
    btnBuiltInPanTo.setEnabled(true);
    btnCustomizedPanTo.setEnabled(false);

    // activate the MapOverlay to BLOCK Built-In navigation, and manage customized panTo
    mapEventsOverlay.setActive(true);
    
    // Turn OFF animation to improve customized PANTO - Here !!
    map.setAnimationMode(AnimationMode.NONE);
   }
  });
  toolBar.add(btnCustomizedPanTo);


Note: when using the NavigatorOverlay, if the MapEventsOverlay is activated, then all the actions are performed without animation... so it's necessary to manage the Animation Mode at the NavigatorOverlay level !


full sample code is attached.

View solution in original post

0 Kudos
5 Replies
CarlosColón-Maldonado
Occasional Contributor III
I'm afraid is not as simple as you tried it, and not as simple to discuss here. For starters, any MapOverlay extension does not need to contain a JMap because its super already contains it. Simply call getMap(). It'll probably be null if it has not been added to the JMap on main.

If you want to know how it is done, look at the code! The SDK which comes with the toolkit jar also comes with the source files for the toolkit. You have to associate the source file to the referenced toolkit on your project though eclipse in order to see it. On this post, I mentioned how to do that. I'm enclosing the source file of the NavigatorOverlay class which is rather complicated but can be studied.

P.S. Nice touch on your "onMouseWheelMoved" method. I'll be using that to override mouse wheel actions I need.
0 Kudos
JeremieJoalland1
Occasional Contributor II
I've already taken a look to the navigatorOverlay source code, and it's definitly not so easy... but i didn't find anything which could explain how I could perform a smooth customized panTo.
but i will look at toolkit source code again, based on your suggestions.

my second observation was that it seems that built-in panTo doesn't fire any JMap public event (zoom, setExtent, panTo, etc.) so I'm not confident it is possible to developed a customized panTo in few days and would like to have feedback from ArcGIS Runtime expert on this point.
0 Kudos
EliseAcheson1
Occasional Contributor
Is it possible to implement a customized PanTo which has the same nice and smooth behavior than built-in ?


Hi,

By built-in you mean the map panning that happens when you pan with the mouse? This is different from 'PanTo', like the panTo method we expose in the API to pan to a geometry.

I need to ask if you like the 'nice and smooth' mouse panning that comes with the JMap why you are trying to re-write it yourself?

The navigator overlay responds to mouse clicks on the navigator's pan buttons and uses the public method 'panTo(Geometry)' I mention above. It's meant for this type of pan from a current extent to a new extent based on the geometry passed in, rather than the fairly continuous panning that happens when you navigate with the mouse.

So the short answer is that the methods used internally for each type of panning are different and the internal logic for mouse panning is not exposed via the API so you probably can't re-write it yourself, no.

~elise
0 Kudos
JeremieJoalland1
Occasional Contributor II
thanks for lighting me up on this navigation issue.

I definitly like the nice and smooth mouse panning coming with JMap, and personaly I would like to keep it that way, but my need to re-write it comes from the fact that I'm developing under such constraints for a generic and interal API. Our API expose navigation PanTo and Zoom functions and could work with different technologies...
So I'm developing a kind of ArcGIS Runtime adaptor, and for now I have to block the built-in panning/zooming, and try to re-write it.

I understand that the panning logic is not exposed and is not similar to panTo, but I still get the issue that behavior of my map is not good enough with my customized panTo() function.

I improved it, by turning off the JMap Animation Mode (just find out), but my map is still not attached to the mouse when I call panTo() on mouseDragged() event... while moving the mouse very slowly !
and it's quite frustating, as some more limited open source solution are doing this right (like Geotools).
0 Kudos
JeremieJoalland1
Occasional Contributor II
I finally come with an acceptable panTo() function 😄
- First, the disable animation mode did a lot.
- Second, my code in onMouseDragged() was not good regarding the Point I calculated to panTo, and I corrected it.

in my customized MapEventsOverlay, I changed this :

private java.awt.Point  startPointOnScreen;

...

    @Override
    public void onMouseDragged(MouseEvent event) {
  // do not supper() to block the built-in navigation
     java.awt.Point pointScreen = event.getLocationOnScreen();
     Point endPointOnMap = this.getMap().toMapPoint(pointScreen.x, pointScreen.y);
     Point startPointOnMap = this.getMap().toMapPoint(startPointOnScreen.x, startPointOnScreen.y);
     
     double dx = endPointOnMap.getX() - startPointOnMap.getX();
     double dy = endPointOnMap.getY() - startPointOnMap.getY();
     
     // PAN TO new extent
     Point currentCenter = this.getMap().getExtent().getCenter();
     Point newCenter = new Point(currentCenter.getX() - dx, currentCenter.getY() - dy);
     
     this.getMap().panTo(newCenter);      
     
     this.startPointOnScreen = pointScreen;
    }

...

    @Override
    public void onMousePressed(MouseEvent event) {
  // do not supper() to block the built-in navigation
     this.startPointOnScreen = event.getLocationOnScreen();
    }

...

    @Override
    public void onMouseWheelMoved(MouseWheelEvent event) {
  // do not supper() to block the built-in navigation
  java.awt.Point pointScreen = MouseInfo.getPointerInfo().getLocation();
  // java.awt.Point pointScreen = event.getLocationOnScreen(); // => always return 0,0
  Point pointCenter = this.getMap().toMapPoint(pointScreen.x, pointScreen.y);
  
  // Turn ON animation - Add this to keep^a nice & smooth zoomIn/Out
  this.getMap().setAnimationMode(AnimationMode.ANIMATE);
  
  int notches = event.getWheelRotation();
  if (notches < 0) {
   // Zoom in
   this.getMap().zoom(1/factor, pointCenter);
  } else {
   // Zoom out
   this.getMap().zoom(factor, pointCenter);
  }
  
  // Turn OFF animation - Add this to keep^a nice & smooth zoomIn/Out
  this.getMap().setAnimationMode(AnimationMode.NONE);
    }


in my application code, I turn off/on the animation mode in action of my 2 buttons, depending if I want built-in panning or my customized panTo :

// Desactivate Built-in navigation
  btnCustomizedPanTo = new JButton();
  btnCustomizedPanTo.setText("ACTIVATE customized panTo");
  btnCustomizedPanTo.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
    btnBuiltInPanTo.setEnabled(true);
    btnCustomizedPanTo.setEnabled(false);

    // activate the MapOverlay to BLOCK Built-In navigation, and manage customized panTo
    mapEventsOverlay.setActive(true);
    
    // Turn OFF animation to improve customized PANTO - Here !!
    map.setAnimationMode(AnimationMode.NONE);
   }
  });
  toolBar.add(btnCustomizedPanTo);


Note: when using the NavigatorOverlay, if the MapEventsOverlay is activated, then all the actions are performed without animation... so it's necessary to manage the Animation Mode at the NavigatorOverlay level !


full sample code is attached.
0 Kudos