May 01

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

Tag: Ext,Gwt,Java,Javascript,Maps,Programming,WebAbhijeet Maharana @ 5:54 pm

Lat and Lon info being shown in a message box
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.

8 Responses to “Gwt-Ext and Google Maps – II (handle click)”

  1. Weekly GWT Links For 5/3/08 | GWT Site says:

    [...] Gwt-Ext and Google Maps II (handle click) Abhijeet Maharana has written another blog post on using Gwt-Ext’s Google Maps functionality. [...]

  2. Travelbanana dev » Blog Archive » Gwt-ext, Kartor klara says:

    [...] 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/ [...]

  3. Gregory Mace says:

    very nicely done. How would you add a listener for a Marker as well?

  4. Abhijeet Maharana says:

    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

  5. Afonso says:

    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.

  6. Abhijeet Maharana says:

    Thanks Afonso … makes me feel good :)
    Have a good day!

  7. loopzy says:

    Great read! thx

  8. Ali says:

    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

Leave a Reply