May 01
Gwt-Ext and Google Maps – II (handle click)

This post is related to my earlier post on Gwt-Ext and Google Maps. While browsing the Gwt-Ext forum, I came across this thread with a simple-looking question from Martin: How can I get the LatLonPoint from a map when a user clicks on the map ??
I looked at the available methods to see if I could figure this out. When I realized I wasn’t getting anywhere, I tried looking at the source to figure out what was going on. I found that Mapstraction does have a facility to register callback functions for events. Below code snippets are from mapstraction.js.
1 2 3 4 5 6 7 8 9 10 11 | Mapstraction.prototype.addEventListener = function(type, func) { var listener = new Array(); listener.push(func); listener.push(type); this.eventListeners.push(listener); switch (this.api) { case 'openlayers': this.maps[this.api].events.register(type, this, func); break; } } |
When the callback functions registered for ‘click’ event are invoked, they are supplied with a LatLonPoint instance with the latitude and longitude information of the location which was clicked. See line 4 below.
1 2 3 4 5 6 7 | Mapstraction.prototype.clickHandler = function(lat, lon, me) { for (var i = 0; i < this.eventListeners.length; i++) { if (this.eventListeners[i][1] == 'click') { this.eventListeners[i][0](new LatLonPoint(lat, lon)); } } } |
However, this argument is lost because of the way Gwt-Ext API exposes this functionality: MapPanel class registers a function with no parameters. Below code snippet is from MapPanel.java. Note the native method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public void addEventListener(final String event, final Function listener) { if (!mapRendered) { addListener(MAP_RENDERED_EVENT, new Function() { public void execute() { doAddEventListener(event, listener); } }); } else { doAddEventListener(event, listener); } } private native void doAddEventListener(String event, Function listener) /*-{ var map = this.@com.gwtext.client.widgets.map.MapPanel::mapJS; map.addEventListener(event, function() { listener.@com.gwtext.client.core.Function::execute()(); }); }-*/; |
The solution is to override these two methods. For that we need an interface with an execute() method that can accept arguments. I added an interface ‘OneArgFunction’ that does this. We need a proper fix for this so that we can handle more arguments. For now, a one-argument method will suffice.
1 2 3 4 5 | package com.maharana.gwtextmaps.client; public interface OneArgFunction { public void execute(com.google.gwt.core.client.JavaScriptObject arg); } |
In the overridden methods below, I register a function which accepts the LatLonPoint instance as parameter and hands it over to the execute() method for further processing. Then I invoke the overridden addEventListener() to register an event handler that places a new marker and centers the map on the clicked location. GoogleMap inherits from MapPanel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | mapPanel = new GoogleMap() { public void addEventListener(final String event, final OneArgFunction listener) { if (!this.isRendered()) { addListener(MAP_RENDERED_EVENT, new Function() { public void execute() { doAddEventListener(event, listener); } }); } else { doAddEventListener(event, listener); } } private native void doAddEventListener(String event, OneArgFunction listener) /*-{ var map = this.@com.gwtext.client.widgets.map.MapPanel::mapJS; map.addEventListener(event, function(llp) { listener.@com.maharana.gwtextmaps.client.OneArgFunction::execute(Lcom/google/gwt/core/client/JavaScriptObject;)(llp); }); }-*/; // constructor - attach event listener { addEventListener("click", new OneArgFunction(){ public void execute(JavaScriptObject arg) { LatLonPoint llp = new LatLonPoint(arg); mapPanel.setCenterAndZoom(llp, mapPanel.getZoom()); mapPanel.addMarker(new Marker(llp)); MessageBox.alert("Clicked Location", "Lat: " + llp.getLat() + "<br>Lon: " + llp.getLon()); } }); } }; |
This does the trick. I am not using any Google Maps specific code here so it should work for other providers as well. Do let me know if I have missed something obvious or got something wrong.
I have modified the demo I posted in my earlier blog entry to include this. You can download it from Gwt-Ext.com or Rapidshare.

May 4th, 2008 at 6:32 am
[...] Gwt-Ext and Google Maps II (handle click) Abhijeet Maharana has written another blog post on using Gwt-Ext’s Google Maps functionality. [...]
May 20th, 2008 at 9:48 pm
[...] Idag har vi fixat så att man kan söka efter platser som laddas dynamiskt samt trycka på kartan och få fram koordinater (vilket ska användas då man lägger till nya platser i Wikin). Precis som innan använder vi oss av Gwt-ext. Detta ledde till ett litet problem då viss funktionalitet saknades och vi blev tvungna att ladda över lite funktioner i klassen MapPanel.java. Med hjälp av en tutorial lyckades vi lösa problemet, http://abhijeetmaharana.com/blog/2008/05/01/gwt-ext-and-google-maps-ii-handle-click/ [...]
January 15th, 2009 at 12:53 pm
very nicely done. How would you add a listener for a Marker as well?
January 15th, 2009 at 1:57 pm
Hi Gregory,
Thanks for dropping by. This should help: http://gwt-ext.com/forum/viewtopic.php?f=7&t=1727&p=6003&hilit=marker+listener
July 6th, 2009 at 10:02 pm
Man this is great.You saved a LOT of people hours and hours of really big trouble. Very good tutorial, works and it is well explained. Thank you so much.
July 8th, 2009 at 11:49 pm
Thanks Afonso … makes me feel good
Have a good day!
December 3rd, 2009 at 8:41 pm
Great read! thx
February 26th, 2010 at 12:53 am
I am using GoogleMarker in my application. First problem that I faced was that the GoogleMarker is not draggable. In order to make it draggable, I created another class MyGoogleMarker extending GoogleMarker and added the following function.
public native void enableDragging() /*-{
var marker = this.@com.gwtext.client.core.JsObject::getJsObj()();
marker.setDraggable(true);
}-*/;
It solved the problem. Now, the marker is draggable. Now the next issue is that I want to track the dragging marker. So I would need a listener so added the following function to MyGoogleMarker.
public native void addEventListener(String event, MyEventHandler listener) /*-{
var marker = this.@com.gwtext.client.widgets.map.Marker::toGoogle()();
marker.addListener(event, function(llp) {
listener.@com.findmeacab.project.client.MyEventHandler::execute(Lcom/google/gwt/core/client/JavaScriptObject;)(llp);
});
}-*/;
Where MyEventHandler is the following interface.
package com.findmeacab.project.client;
import com.google.gwt.core.client.JavaScriptObject;
public interface MyEventHandler {
public void execute(JavaScriptObject arg);
}
I do not know where I am making mistake the event never triggers when I make the call.
MyEventHandler myEventHandler = new MyEventHandler() {
@Override
public void execute(JavaScriptObject arg) {
System.out.println(“Drag End”);
}
};
marker.addMyEventListener(“dragend”, myEventHandler);
Any suggestion will be highly appreciated
-Ali