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 rapidshare.com.

One Response 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. […]

Leave a Reply