2013/12/04

Migrating a Google Web Toolkit Google Maps v2 app to v3

It's been quite a long time since I wrote my last post, but I'm involved in the creation of a new startup, which is consuming almost all the time I have. Anyway, in this process we had to face a situation that required migrating some code that was using Google Maps API v2 to v3 in a Google Web Toolkit project. Let's see how to do this.

V3 here we go!
Google Maps API v3

You may know already Google has decided to discontinue support for legacy apps using previous v2 version. V3 has been around for quite a long time already, but you may have some legacy GWT app that is still using v2. If that is the case, then keep reading on, I'll show how we migrated to v3, well, the non-official version of the v3 API for GWT (more on this later...).

In our case, we have a small prototype that, when we started developing it, we used Google Maps v2 not because we didn't want to, but mostly because there is really no official port to v3 from Google, thus, what you can find in the GWT site is the v2 library. Thus, at the time when we started, we used the available version:

Maps v2 API 1.1.1 

This API worked fine, it is obviously obselete, and it looks weird that it is still available for downloading, nothing states that is discontinued, leading to confusion, in my opinion. You can also find the official port to v3 in that page, but last build was from more than a year ago.

You bet


That said, you basically have two options here: go for the "official" version of the v3, from Google or choose the non-so official version of the Google Maps v3 API. We used both, as for the first one, we cannot recommended it for two reasons mostly. First, it looks to be discontinued...I'm not saying is dead...but its supposed to be an early release, and hasn't been updated since March 2012...suspicious, or the guy developed the most stable beta release ever, no bugs!. Second, it lacks some of the features from the unofficial version, also there is no discussion group either...thus your pleas for help will be entering the eternal void very quickly.

For those reasons, we decided to use the unofficial version. The project is alive and well maintained, it has a strong user community, and you can benefit from the extended examples available. That said, let us depict the migration process from now on.

Migration: the funny parts

If you already migrated a Javascript app to v3, you can more or less estimate the effort regarding the migration from v2 to v3. It would've been very nice that the new v3 API was backward compatible with v2 legacy code...but this is not the case.

Is not really that hard tought, in our case it took us something like 3-4 hours time, but we don't use Google Maps that much, neither use complex features (StreetView, Routes, Traffic, etc), if you're using those, probably you'll have to spend more time on this. Let's see the important points that we identified:

  • MapWidget class does not have a constructor that you can pass a latitude and longitude pair, you will need to provide a MapOptions or use the Factory method to get a MapWidget object. So the old version would be like this:

mapMarinePark = new MapWidget(LatLng.newInstance(52.62715,1.7734), 2);

New version should be:

LatLng center = LatLng.newInstance(52.62715,1.7734);
MapOptions opts = MapOptions.newInstance();
opts.setZoom(9);
opts.setCenter(center);
opts.setMapTypeId(MapTypeId.HYBRID);
MapTypeControlOptions controlOptions = MapTypeControlOptions.newInstance();
controlOptions.setMapTypeIds(MapTypeId.values()); // use all of them
controlOptions.setPosition(ControlPosition.TOP_RIGHT);
opts.setMapTypeControlOptions(controlOptions);
mapMarinePark = new MapWidget(opts);
mapMarinePark.setSize("100%", "100%");


Or use the factory method if you're creating the map from an existing Element in DOM:

Element element = DOM.getElementById("myexistingmap");
MapImpl existingMap = MapImpl.newInstance(element, opts);
mapMarinePark = MapWidget.newInstance(existingMap);


  • The Marker constructor does not allow LatLng passed as argument no longer. You'll have to use the factory method to create a new Marker.


V2 way to create a new Marker:

Marker marker = new Marker(Double latitude, Double longitude, MarkerOptions options);


New way to create a marker:

Marker marker = Marker.newInstance(MarkerOptions options);

  • You can't use the Icon class to specify the Marker image, instead you'll have to use the MarkerImage class. Also you can't use the MapWidget.addOverlay() method any longer to add markers to the Map. Instead, you'll have to user the Marker.setMap(MapWidget map) method.

Old way:

Icon icon = Icon.newInstance("assets/img/itsaseus_buoy.png");
icon.setIconSize(Size.newInstance(32, 32));
options.setIcon(icon);
Marker marker = new Marker(latitude, longitude, options);
map.addOverlay(marker);


New way:

MarkerOptions options = MarkerOptions.newInstance();
MarkerImage icon = MarkerImage.newInstance("assets/img/water.png");
icon.setScaledSize(Size.newInstance(32, 37));
options.setIcon(icon);
options.setTitle(buoy.getBuoyName());
String location[] = buoy.getLocation().split(" ");
LatLng center = LatLng.newInstance(latitude, longitude);
Marker marker = Marker.newInstance(options);
marker.setPosition(center);
marker.setMap(map);

  • The way to add events handlers to the Marker class has changed too. You can get the same functionality, more or less.

Old way to add handlers:

MarkerClickHandler mch = new MarkerClickHandler() {
    public void onClick(MarkerClickEvent event) {
 //Do your magic here  
    }
};

And the new way to deal with those:

marker.addClickHandler(new ClickMapHandler() {
    public void onEvent(ClickMapEvent event) {
        //Do your stuff
    }
});

  • Last thing we noticed in our case was, if you plan to use Polygons in your map, we were calculating the enclosing area of those Polygons. This has also changed, previously the Polygon class itself had a method called getArea that is no longer valid.
Old way to calculate the Area and convert it to km²:

Double area = polygon.getArea();
String converted = (""+(area/1000000)).replace(".",",");
converted = converted.substring(0,converted.indexOf(",")+3);

And the new way...
Double area = SphericalUtils.computeArea(poly.getPath());

Probably, as I said previously, there are more complicated scenarios where you will have to deal with more complicated code translatios, but so far, those are the ones we faced.


Final word

As a conclusion, I'd like to warn you about using GWT for any serious project. To us, it looked like a very nice solution, coming from a Java background, to have something up&running quickly. For a long shot, I don't recommend it: the project seems to have not a lot of activity, people might say that this is because its a mature technology...well, there are other examples of mature technologies that are constantly being updated and generate a lot of activity in their forums (Doxygen for instance). Also, I had the feeling that I never had total control of what is going on "under the hood". Sure, coding in Java and automatically translating to JS is cool, but it comes with a payoff. Lastly, there are alternatives, some of them managed by Google: Dart, Go or even AngularJS, that will provide you with full control of what you're doing. Actually, we're switching to AngularJS at this very moment. For the server side we're using Python-Django.


No comments:

Post a Comment