Skip to main content

Integrating Maps into Your Java Web Application with Google Maps and Ajax

October 26, 2006

{cs.r.title}






Interactive maps are becoming more and more widespread in modern
web applications. Potential uses abound: real estate and tourism
are two obvious domains where interactive maps can provide real
added value, but there are dozens of others as well. However,
although many of today's web applications could make good use of
integrated mapping software, development teams often shy away from
the complexity of integrating a full-fledged geographical database
system. Indeed, while clickable maps have been around for years,
truly interactive maps, which interact in real time with a server
application, have always been considerably more complex. Until
now.

Two relatively recent technologies, Google Maps and Ajax, can
make life considerably easier in this area. In this article, we
will discuss how you can easily implement dynamic interactive maps
on your Java web application, using the Google Maps API for the web
interface, and Ajax to provide real-time interaction with the
server. This article is designed to give you a rapid introduction
to both Google Maps and Ajax, and to let you get your own
interactive maps up and running quickly and easily.

A Brief Introduction to Google Maps

In the first part of this article, we will discuss how to
integrate a feature-rich map into your application in record time,
by using the Google Maps API. The Google Maps API is an easy-to-use
JavaScript API that enables you to embed interactive maps directly
in your application's web pages. And as we will see, it is easy to
extend it to integrate real-time server requests using Ajax.

Getting started with the Google Maps API is easy. There is
nothing to download; you just need to sign up to obtain a key to
use the API. There is no charge for publicly accessible sites (for
more details, see the "http://www.google.com/apis/maps/signup.html">Sign up for the
Google Maps API
page). You need to provide the URL of your website, and, when your application is deployed on a website, your key will only work
from this URL. One annoying thing about this constraint is that you
need to set up a special key to use for your development or test
machines: for the sample code, I had to create a special key for
http://localhost:8080/maps, for example.

Once you have a valid key, you can see the Google Maps API in
action. Let's start off with something simple: displaying a map on
our web page. Suppose you have been commissioned by the Ministry of
Tourism of Elbonia to build a promotional website about the many
tourist attractions of Elbonia, and in particular its renowned hot
mud baths. Note: In our example, since Elbonia is difficult to
find on current maps, we will display a map of New Zealand, a small
island nation in the middle of the South Pacific ocean.

Although the API is not particularly complicated, working with
Google Maps requires a minimal knowledge of JavaScript. You also
need to know the latitude and longitude of the area you want to
display. If you're not sure, you can find this sort of information
on the internet, or even by looking in an atlas!

The full code listing of our first Google Map is shown here:

[prettify]
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Our first Google Map</title>
        <script src="http://maps.google.com/maps?file=api&v=2&key=MYKEY"
            type="text/javascript"></script>
        <script type="text/javascript">

        //<![CDATA[

        function load() {
            if (GBrowserIsCompatible()) {
                var map = new GMap2(document.getElementById("map"));
                map.setCenter(new GLatLng(-41.5, -185), 5);
            }
        }
        //]]>
        </script>
    </head>
    <body onload="load()" onunload="GUnload()">
        <div id="map" style="width: 420px; height: 420px"></div>
    </body>
</html>
[/prettify]

The first thing to notice here is the code that fetches the
actual JavaScript code from the Google Maps server. You need to
supply your key here for the code the work.

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

Next comes the code that actually downloads the map from the
server.

[prettify]
<script type="text/javascript">
//<![CDATA[
    function load() {
        if (GBrowserIsCompatible()) {
            var map = new GMap2(document.getElementById("map"));
            map.setCenter(new GLatLng(-41.5, -187.5), 5);
        }
    }
//]]>
</script>
[/prettify]

You create a GMap2 object, providing a reference to
some element in your page. In this case, we refer to the

element in the page body. You need to
provide the map with a valid latitude and longitude, and the zoom
level. The higher the zoom, the more detailed the map.

Google Maps will work correctly with recent versions of most
modern browsers. Nevertheless, it is good practice to make sure all
the same, by using the GBrowserIsCompatible()
method.

Finally, in the body, we display the map. The size and shape of
the map are taken from the corresponding HTML element. The map is
initialized when the page is loaded (via the onload event). In
addition, when the user leaves the page, the
GUnload() method is called (via the onunload event).
This cleans up the map data structure in order to avoid memory
leak problems that occur in Internet Explorer.

[prettify]
    <body onload="load()" onunload="GUnload()">
    <div id="map" style="width: 420px; height: 420px"></div>
    </body>
[/prettify]

The resulting map is illustrated in Figure 1.

A simple map
Figure 1. A simple map

Panning and Zooming

Now that we can successfully display a map, let's try to add some
zoom functionality. The Google Maps API lets you add a number of
different controls to your map, including panning
and zooming tools, a map scale, and a set of buttons letting you
change between Map and Satellite views. In our example, we'll add a
small pan/zoom control and an "Overview map" control, which places
a small, collapsible overview map. You add controls using the
addControl() method, as shown here:

[prettify]
function load() {
  if (GBrowserIsCompatible()) {
    var map = new GMap2(document.getElementById("map"));
    map.setCenter(new GLatLng(-41.5, -187.5), 5);
    map.addControl(new GSmallMapControl());
    map.addControl(new GOverviewMapControl());
  }
}
[/prettify]

Our new map is illustrated in Figure 2.

A map with some controls
Figure 2. A map with some controls

Adding Markers to Your Maps

The Google Maps API is not just about displaying a map; you can
also configure more dynamic maps. To do this, you add "overlays" to
your map. Overlays are objects that are displayed as certain
locations on the map, and that the user can interact with. A
typical use of an overlay is to place a marker at a given location
to indicate some special place or address.

Let's add a simple marker to our map. We want a marker to be
displayed over a particular tourist attraction in Elbonia. After
obtaining the latitude and longitude of the site, we create a new
GMarker object and add it to the map using the
addOverlay() method:

[prettify]
function load() {
  if (GBrowserIsCompatible()) {
    var map = new GMap2(document.getElementById("map"));
    map.setCenter(new GLatLng(-41.5, -187.5), 5);
    map.addControl(new GSmallMapControl());
    map.addControl(new GOverviewMapControl());
    var point = new GLatLng(-41.28, -185.22);
    marker = new GMarker(point);
    map.addOverlay(marker)
  }
}
[/prettify]

In our example, we want to display some details when a user
clicks on our marker. We just add a listener to the marker, and
invoke the openInfoWindowHtml() method, which displays
an HTML message in a text bubble.

[prettify]
    marker = new GMarker(point);
    map.addOverlay(marker)
    GEvent.addListener(marker, "click", function() {
        marker.openInfoWindowHtml("<p>More details</p>");
    });
[/prettify]

Now that we have gone through the basics of the Google Maps API,
let's see how we can integrate a Google Map with a server-side
application.

An Introduction to Ajax

Ajax is the technology behind Google Maps. It is also the
technology we will use to extend and integrate our Google Map
into a dynamic web application. Ajax stands for "Asynchronous
JavaScript and XML." Behind all the hype, Ajax is basically a
technology that enables JavaScript code to sent a request to a
server, retrieve some data in response (generally in XML format,
hence the X in Ajax), process this data, and update the web page
accordingly. The first A stands for "Asynchronous;" Ajax requests
can sent off to a server in the background, without interrupting
the user. When the results arrive in the client browser, a callback
function is called to process the data and dynamically update the
screen. The screen does not have to be reloaded at each update,
which provides for a much smoother, faster user experience.

At the heart of Ajax is the XMLHttpRequest
class.

[prettify]
    <script language="javascript" type="text/javascript">
        var req = new XMLHttpRequest();
    </script>
[/prettify]

In fact, creating an XMLHttpRequest object is not
quite that simple: the time-old problems of browser compatibility
raise their ugly heads again here. Put simply, Microsoft Internet
Explorer uses one of two possible ActiveXObject objects, whereas
virtually all other browsers use the XMLHttpRequest
class. There are many documented workarounds to this problem, most
of which generally involve something along the following lines:

[prettify]
    <script language="javascript" type="text/javascript">
    <script type="text/javascript">
    //<![CDATA[
    var req = false;
    if (window.XMLHttpRequest) {
        // Create XMLHttpRequest object in non-Microsoft browsers
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        // Create XMLHttpRequest via MS ActiveX
        try {
            // Try to create XMLHttpRequest in later versions
            // of Internet Explorer
            req = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e1) {
            // Failed to create required ActiveXObject
            try {
                // Try version supported by older versions
                // of Internet Explorer
                req = new ActiveXObject("Microsoft.XMLHTTP");
            } catch (e2) {
                // Unable to create an XMLHttpRequest with ActiveX
            }
        }
    }
    </script>
[/prettify]

However, the Google Maps API provides a convenient browser-safe
shortcut, by using the GXmlHttp class:

[prettify]
    <script language="javascript" type="text/javascript">
        var req = GXmlHttp.create();
    </script>
[/prettify]

You use your XMLHttpRequest object to fetch data
from the server. Since Ajax is asynchronous, you don't know when
you will get a response, and your application will not hang around
waiting for the server to answer. So when you send the request, you
also provide a special callback function. The main purpose of this
callback function is to process the returned data. However, because
of the way Ajax does things, it needs to verify the status of the
request beforehand to make sure it has really received the data
correctly.

Here is an example of some code calling an Ajax request. It is
designed to fetch the list of all the cities on the map, in XML
form, in order to display them on the map.

[prettify]
    <script language="javascript" type="text/javascript">
        // Create the XMLHttpRequest object
        var request = GXmlHttp.create();
        // Prepare an asynchronous HTTP request to the server
        request.open("GET", "/maps/SiteDirectory?method=findAllCities", true);
        // Returned data will be processed by this function
        request.onreadystatechange = getCallbackFunction(request, processCityData);
        // Send the query
        request.send(null);
    </script>
[/prettify]

Most of the code is pretty self-explanatory. The key function
here is the getCallbackFunction() function. This
function does some checks on the request's status, and when a
correct response has been received, it calls the
processCityData() function, which contains all the
interesting business logic:

[prettify]
  
    <script language="javascript" type="text/javascript">
        function getCallbackFunction(req, processData) {

          // Return an anonymous function that listens to the
          // XMLHttpRequest instance
          return function () {

            // If the request's status is "complete"
            if (req.readyState == 4) {
              if (req.status == 200) {

                // Pass the XML payload of the response to the
                // handler function
                processData(req.responseXML);

              } else {

                // An HTTP problem has occurred
                alert("HTTP error: "+req.status);
              }
            }
          }
        }

        function processCityData(xmlDoc) {
        // Process the returned XML data
        ...
        }
    </script>
  
[/prettify]

And, for our purposes, that's pretty much all we need to know
for now. In the next section, we will see how you can use Ajax to
add visual markers, updated dynamically from a database, to your map.

Using Ajax to Enhance our Google Maps Application: Dynamic
Markers

Now that we have a working knowledge of the Google Maps API, and
an idea of what Ajax can do for us, let's see how we can integrate a
Google Map with a Java web application using Ajax. In our Elbonian
website, we want to display all the tourist attractions of
Elbonia. You want an interactive map where users can localize a
particular tourist attraction using markers on the map. When they
click on a site, a window pops up with details about that
particular attraction.

The first step is to integrate a set of markers provided by the
server. One way to do this would be to dynamically generate the
JavaScript code on the server. However this is cumbersome, and can
generate excessively large HTML pages. Luckily, there is a better
way. The Google Maps API provides a convenient function called
GDownloadUrl() that lets you download data in XML
form and process it in your JavaScript code. From there, it is
fairly easy to convert your XML data into Google Map Markers.

This Ajax application will be powered on the server by a single
servlet. This servlet will provide data about tourist attractions
and cities containing tourist attractions, in XML form. The details
of this servlet are not particularly important for the purposes of
this discussion; the key thing to remember is that the servlet
returns XML (and not HTML) data based on the query parameters it
receives:

[prettify]

public class TouristSiteDirectoryServlet extends javax.servlet.http.HttpServlet
    implements javax.servlet.Servlet {

    ...
    /**
     * Data Access Object providing search methods on the tourist attraction site database
     */
    private SiteDAO getSiteDAO() {
        ...
    }

    /**
     * Data Access Object providing search methods on the site database
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
        String method=request.getParameter("method");
        XStream xstream = new XStream();
        xstream.alias("marker", Marker.class);
        response.setContentType("application/xml");
        if (method == null) {
            String id = request.getParameter("id");
            Site site = getSiteDAO().findSiteById(id);
            xstream.alias("site", Site.class);
            String xml = xstream.toXML(site);
            response.getWriter().write(xml);
        } else if (method.equals("findAll")) {
            List<Marker> sites = getSiteDAO().findAllAsMarkers();
            String xml = xstream.toXML(sites);
            response.getWriter().write(xml);
        } else if (method.equals("findAllCities")) {
            List<Marker> cities = getSiteDAO().findAllCityAsMarkers();
            String xml = xstream.toXML(cities);
            response.getWriter().write(xml);
        } else if (method.equals("findByCity")) {
            String city = request.getParameter("city");
            List<Marker> sites = getSiteDAO().findByCityAsMarkers(city);
            String xml = xstream.toXML(sites);
            response.getWriter().write(xml);
        }
    }
}

[/prettify]

Again, the code is fairly self-explanatory. The servlet
basically queries the database via the DAO object, and the uses the
XStream site to convert the data into XML form. This data can also
be stored in a plain old XML file. Of course, the advantage of
storing geographical site coordinates in a database is that they
can be updated as necessary; for example, in the case of a major
earthquake or land slide, or continental drift, or to keep track of
the famous migrating mud-transporting camel caravans of the central
Elbonian steppes. This XML is then written directly to the servlet
response stream. The resulting lists look something like this:

[prettify]
  
    <list>
        <marker>
            <id>3</id>
            <name>The Famous Mud Baths of central Elbonia</name>
            <latitude>-36.87</latitude>
            <longitude>185.22</longitude>
        </marker>
        <marker>
            <id>5</id>
            <name>The North Elbonian Mud Baths</name>
            <latitude>-36.77</latitude>
            <longitude>185.92</longitude>
        </marker>
        ...
    </list>
  
[/prettify]

On the client side, we fetch the XML marker list, and generate a
set of corresponding markers on the map. To do this, we use Ajax
via a Google utility class: GXmlHttp. This class sends
a query to the server to fetch an XML document, which you can then
parse using the JavaScript DOM functions.

Let's look at how this is done in a real example. The XML data
shown above is fetched by the loadSites() function,
which invokes the processSiteData() function to, well,
process the site data:

[prettify]
  
    <script language="javascript" type="text/javascript">
        function loadSites() {
            var request = GXmlHttp.create();
            request.open("GET", "/maps/SiteDirectory?method=findAll", true);
            request.onreadystatechange = getCallbackFunction(request, processSiteData);
            request.send(null);
        }
    </script>
  
[/prettify]

The processSiteData() function, and the associated
displaySiteMarkers() function, use JavaScript DOM processing
techniques to extract useful data about tourist attraction sites,
and to create the corresponding markers on the map:

[prettify]
  
    <script language="javascript" type="text/javascript">
        function processSiteData(xmlDoc){
            // obtain the array of markers and loop through it
            siteMarkers = xmlDoc.documentElement.getElementsByTagName("marker");
            displaySitesMarkers();
        }

        function displaySitesMarkers() {
            map.clearOverlays();
            for (var i = 0; i < siteMarkers.length; i++) {
                // obtain the attributes of each marker
                var lat = parseFloat(siteMarkers[i].getElementsByTagName("latitude")[0].firstChild.nodeValue);
                var lng = parseFloat(siteMarkers[i].getElementsByTagName("longitude")[0].firstChild.nodeValue);
                var id = siteMarkers[i].getElementsByTagName("id")[0].firstChild.nodeValue;
                var label = siteMarkers[i].getElementsByTagName("name")[0].firstChild.nodeValue;
                createSiteMarker(new GLatLng(lat,lng),label,id);
            }
        }
    </script>
  
[/prettify]

Using Ajax to Enhance our Google Maps Application: On-Demand
Details

When a user clicks on a site, we want an information box to pop
up with details about the site (see Figure 3). This is a
frequently needed requirement in map-based applications.

Displaying details
Figure 3. Displaying details

However, in real-world applications, there is often too much
data to load all at once. You need to fetch and display in on an
"on-demand" basis. To do this, we will use Ajax once again. First,
we add a listener to the markers, calling the
openInfoWindow() function:

[prettify]
  
    <script language="javascript" type="text/javascript">
        GEvent.addListener(marker, "click", function() {
            openInfoWindow(marker,"" + id);
        });
    </script>
  
[/prettify]

The openInfoWindow() function displays a temporary
message ("Loading details...") and then proceeds to fetch the
detailed information from the server. The temporary message is
important as a visual cue for the user, in order to let the user
know the request is being processed.

[prettify]
  
    <script language="javascript" type="text/javascript">
        function openInfoWindow(marker, id) {
           marker.openInfoWindowHtml("Loading details...");
           fetchDetails(id,marker);
        }
    </script>
 
[/prettify]

The fetchDetails() function is very similar to the
previous Ajax code we have seen, as is the
displayDetails() function, which uses JavaScript DOM
to extract some useful fields and then prepares HTML details
text to be displayed in the information window:

[prettify]
  
    <script language="javascript" type="text/javascript">
        function fetchDetails(id) {
          var req = GXmlHttp.create();
          req.open("GET", "/maps/SiteDirectory?id="+id, true);
          req.onreadystatechange = getCallbackFunction(req, displayDetails);
          req.send(null);
        }

        function displayDetails(siteDetailsXML) {
             // Get the root "site" element from the document
             var site = siteDetailsXML.getElementsByTagName("site")[0];
             var name = getNodeValue(site.getElementsByTagName("name")[0]);
             var id = getNodeValue(site.getElementsByTagName("id")[0]);
             var symbol = getNodeValue(siteDetailsXML.getElementsByTagName("symbol")[0]);
             var website = getNodeValue(siteDetailsXML.getElementsByTagName("website")[0]);
             var address = site.getElementsByTagName("address")[0]
             var address1 = getNodeValue(siteDetailsXML.getElementsByTagName("line1")[0]);
             var address2 = getNodeValue(siteDetailsXML.getElementsByTagName("line2")[0]);
             var city = getNodeValue(siteDetailsXML.getElementsByTagName("city")[0]);
             var postcode = getNodeValue(siteDetailsXML.getElementsByTagName("postcode")[0]);

            marker = getMarker(id);
            marker.showMapBlowup();
            var html = '<span class="site-title-line">'
                       + name + ' (' + symbol + ')'
                       +'</span>'
                       + '<span class="site-details-line">'
                       + address1
                       +'</span>'
                       + '<span class="site-details-line">'
                       + address2
                       +'</span>'
                       + '<span class="site-details-line">'
                       + city + ' ' + postcode
                       +'</span>'
                       + '<span class="site-details-line">'
                       + '<a href="' + website + '">' + website + '</a>'
                       +'</span>'
            marker.openInfoWindowHtml(html);
        }    </script>
  
[/prettify]

Conclusion

There are obviously many more advanced topics that could not be
discussed in an introductory article like this one. For example, in
a real-world application of this type, there will probably be too
many sites (or tourist attractions, or houses for sale, or
whatever) to comfortably show on a small-scale map. So, at the
start, when the whole country is being displayed, we only want
markers for the cities we are interested in. When a user clicks on
a city, the map will zoom in and display the sites in that city.
This sort of functionality is fairly easy to implement using other
Google Map API features not discussed here, but examples
can be found in the sample code.

Hopefully, this article has given you a good picture of how to
use Google Maps and Ajax to integrate non-trivial map-based
functionalities into your web application. As an introductory
article, the server-side Ajax techniques presented here are fairly
basic. For more sophisticated applications, Ajax frameworks such as
DWR are well worth looking
into.

Resources


width="1" height="1" border="0" alt=" " />
John Ferguson Smart is a freelance consultant specialising in Enterprise Java, Web Development, and Open Source technologies, currently based in Wellington, New Zealand.
Related Topics >> Web Services and XML   |