May 01 2008
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.
