My favorites | English | Sign in

Google Maps API

Converting a Maps API Mashup into a Mapplet

Pamela Fox, Geo API Team
May 2007

Introduction

Google Mapplets allow developers to create Google Gadgets that can be loaded by Google Maps users onto the main "shared" map along with other Mapplets, enabling the Google Maps user to easily create "mashups of mashups." They're a great way to reach a large audience of users, so most maps developers should consider creating Mapplet versions of their Maps API mashups. With this conversion guide, you should be able to convert your Maps API site into a Mapplet (in 60 minutes or less!). Please consult the Mapplets documentation for information on using the Developer Gadget to test that your Mapplet is working on Google Maps.

Converting HTML to Google Gadget XML

A Google Gadget is actually just JavaScript, HTML, and CSS tags inside an XML wrapper. In order to convert your Maps API site into a Mapplet, you should do the following:

  • Replace your HTML/HEAD/BODY tags with the XML that defines a Google Gadget. The root tag is named <Module>, and all your JavaScript/CSS/HTML now goes inside the <Content> tag. (Click to highlight)
  • Arrange your JavaScript, HTML, and CSS carefully within the <Content> tag. The order that will generally produce the least problems down the road is: <style> tags first, HTML next, <script> tags last.
  • Refactor any map functions that you normally associate with a load function. Since the map is loaded synchronously before your Mapplet code is run, you don't need to encapsulate your map functions within a load handler. If you structure your Mapplet as recommended above, all page elements will be loaded before your script runs; you should be able to safely remove the load function wrapping your code. (Click to highlight)

Using the Shared Map

  • Add a <Require feature="sharedmap"> in your <ModulePrefs> tag so that your Google Gadget can access the Mapplet-specific version of the Maps API. (Click to highlight)
  • Remove the script tag that loads in the normal Maps API script. The Google Gadget XML is now loading the Mapplet version of the Maps API for you from the sharedmap feature. (Click to highlight)
  • Remove the div that you previously used to load your map into. You're sharing the main map now with other Mapplets, so no div placeholder is needed. (Click to highlight)
  • Remove the argument from the GMap2 constructor. You don't need to tell the constructor where to load the map since the shared map is already loaded. (Click to highlight)
  • Remove the GBrowserIsCompatible check if you're using it now. If a user has loaded your Mapplet onto the map, you can safely assume they're using a browser compatible with Google Maps. (Click to highlight)
The Maps API Site:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>

   <meta http-equiv="content-type" content="text/html; 
charset=utf-8"/>

   <title>Google Maps AJAX + mySQL/PHP 
Example</title>

    <script src="http://maps.google.com/maps?file=api&v=2&key=abc123"
      type="text/javascript"></script>
    <script type="text/javascript">

    //<![CDATA[

    function load() {

      if (GBrowserIsCompatible()) {
        var map = new GMap2(document.getElementById("map"));
        map.setCenter(new GLatLng(37, -122));

        }

     }
    //]]>
  </script>

  </head>
  <body onload="load()" onunload="GUnload()">

   <h2> My Really Simple Map </h2>

    <div id="map" style="width: 500px; height: 
300px"></div>
  </body>

</html>
The Mapplet:

<?xml version="1.0" encoding="UTF-8"?>

<Module>
<ModulePrefs title="My Simple Map" 
             description="A converted maps api example"
             author="Your name"
             author_email="your-email@google.com"
             height="150">
  <Require feature="sharedmap"/>

</ModulePrefs>

<Content type="html">
<![CDATA[

<h2>My Simple Map</h2>

<script>
  var map = new GMap2();
  map.setCenter(new GLatLng(37, -122));
</script>


]]>
</Content>
</Module>

Using Asynchronous Map Functions

Since we're using a shared map now, some of the maps function calls for retrieving information are now asynchronous—that means that the return value isn't returned to the code instantly but instead is passed into a callback function. If you are currently using a function that is now asynchronous in Mapplets, just modify your code so that everything that happened after that function (and was dependent on the result of it) is now encapsulated inside the callback function parameter.

The affected functions and their asynchronous versions are listed below. You can read more about them in the Mapplets reference.

GMap
getMapTypes()getMapTypesAsync(callback)
getCurrentMapType()getCurrentMapTypeAsync(callback)
isLoaded()isLoadedAsync(callback)
getCenter()getCenterAsync(callback)
getBounds()getBoundsAsync(callback)
getBoundsZoomLevel(bounds)getBoundsZoomLevelAsync(bounds, callback)
getSize()getSizeAsync(callback)
getZoom()getZoomAsync(callback)
getInfoWindow()getInfoWindowAsync(callback)
fromLatLngToDivPixel(latlng)fromLatLngToDivPixelAsync(latlng, callback)
fromDivPixelToLatLng(pixel)fromDivPixelToLatLngAsync(pixel, callback)
fromContainerPixelToLatLng(pixel)fromContainerPixelToLatLngAsync(pixel, callback)
GMarker:
getIcon()getIconAsync(callback)
getPoint()getPointAsync(callback)
isHidden()isHiddenAsync(callback)
GPolygon/GPolyline:
getVertexCount()getVertexCountAsync(callback)
getVertex(index)getVertexAsync(index, callback)
GClientGeocoder
getLatLngAsync(address, callback)getLatLng(address, callback)
getLocationsAsync(address, callback)getLocationsAsync(address, callback)

Here's an example from the documentation that uses map.getCenter() and needs to be modified to use the asynchronous version of the function.

var map = new GMap2(document.getElementById("map"));
GEvent.addListener(map, "moveend", function() {
  var center = map.getCenter();
  document.getElementById("message").innerHTML = center.toString();
});
map.setCenter(new GLatLng(37.4419, -122.1419), 13);

In the Mapplet version, we replace getCenter() with getCenterAsync(), and put the code that was supposed to run after that inside a callback function.

var map = new GMap2();
GEvent.addListener(map, "moveend", function() {
    map.getCenterAsync( function(center) {
    document.getElementById("message").innerHTML = center.toString();
  });
});
map.setCenter(new GLatLng(37.4419, -122.1419), 13);

Making AJAX Calls the Mapplet Way

Currently, the Google Maps API methods for making AJAX calls (GXml, GXmlHttp, GDownloadUrl) are not supported in the Mapplets API. That's okay though, because the Google Gadgets API already has functions to help you get remote content into your app:

  • _IG_FetchContent(url, func)
  • _IG_FetchXmlContent(url, func)
  • _IG_FetchFeedAsJSON(url, func, num_entries, get_summaries)

The Google Gadget functions differ slightly in the XML they accept and their caching behavior, so please read about those differences here. Additional information about the functions is available in the Google Gadgets documentation.

Most likely, your maps application is using GDownloadUrl to download an XML file, like in the following example:

GDownloadUrl("markers.xml", function(data) {
  var xml = GXml.parse(data);
  var markers = xml.documentElement.getElementsByTagName("marker");
  for (var i = 0; i < markers.length; i++) {
    var point = new GLatLng(parseFloat(markers[i].getAttribute("lat")),
                            parseFloat(markers[i].getAttribute("lng")));
    map.addOverlay(new GMarker(point));
  }
});


In the Mapplet version, we replace GDownloadUrl with _IG_FetchXmlContent which uses the same parameters but returns an already parsed XML document in the callback, eliminating our need to parse the XML.

_IG_FetchXmlContent("markers.xml", function(data) {
  var markers = data.getElementsByTagName("marker");
  for (var i = 0; i < markers.length; i++) {
    var point = new GLatLng(parseFloat(markers[i].getAttribute("lat")),
                            parseFloat(markers[i].getAttribute("lng")));
    map.addOverlay(new GMarker(point));
  }
});

Removing Non-Mapplet-Compatible Features

Some things aren't allowed to be modified in the main map in order to keep a consistent user interface for all the Mapplets sharing the map.

Some of these features are:

  • Controls: You can't add or remove controls to the map (e.g. map.addControl(new GMapTypeControl())), and along with that, you can't define custom controls (by extending GControl). Custom controls are usually just buttons or legends though, so you can move them off the map and into your Mapplet HTML.
  • Custom Tile Layers: Currently the Maps API functions that enable you to replace the map tiles with your own tiles are not supported within Mapplets due to security concerns. However, you can use GGroundOverlay with a zoom/pan listener for a similar effect.
  • Geocoder Cache: You can use the API-provided geocoder, but you can't create your own Mapplet-specific cache.
  • Custom Overlays: You're currently limited to using only the overlays provided by the API (GGeoXml, GMarker, GPolyline, GPolygon). If you've extended GOverlay in your mashup to create your own custom overlays, you'll either need to replace them with one of the supported overlays or remove them.

Summary

Hopefully, this article has helped you convert your Maps API mashup into a Mapplet. Once you've tested that your Mapplet works in Google Maps (in all browsers), you can submit your mapplet to the Google Mapplets Directory.

For more information, consult the Mapplets Documentation.

If you've run into any issues in creating your Mapplet and have questions, please post in the Google Maps API Group. The Maps API is constantly evolving, so you should stay tuned to the Google Maps API Blog for announcements about new features.