Integrating Maps into Your Java Web Application with Google Maps and AjaxInteractive 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.
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 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:
<!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>
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.
<script src="http://maps.google.com/maps?file=api&v=2&key=MYKEY"
type="text/javascript">
</script>
Next comes the code that actually downloads the map from the server.
<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>
You create a GMap2 object, providing a reference to
some element in your page. In this case, we refer to the
<div> 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.
<body onload="load()" onunload="GUnload()">
<div id="map" style="width: 420px; height: 420px"></div>
</body>
The resulting map is illustrated in Figure 1.

Figure 1. A simple map
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:
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());
}
}
Our new map is illustrated in Figure 2.

Figure 2. A map with some controls
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:
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)
}
}
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.
marker = new GMarker(point);
map.addOverlay(marker)
GEvent.addListener(marker, "click", function() {
marker.openInfoWindowHtml("<p>More details</p>");
});
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.
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.
<script language="javascript" type="text/javascript">
var req = new XMLHttpRequest();
</script>
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:
<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>
However, the Google Maps API provides a convenient browser-safe
shortcut, by using the GXmlHttp class:
<script language="javascript" type="text/javascript">
var req = GXmlHttp.create();
</script>
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.
<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>
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:
<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>
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.
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:
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);
}
}
}
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:
<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>
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:
<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>
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:
<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>
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.

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:
<script language="javascript" type="text/javascript">
GEvent.addListener(marker, "click", function() {
openInfoWindow(marker,"" + id);
});
</script>
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.
<script language="javascript" type="text/javascript">
function openInfoWindow(marker, id) {
marker.openInfoWindowHtml("Loading details...");
fetchDetails(id,marker);
}
</script>
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:
<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>
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.
John Ferguson Smart is a freelance consultant specialising in Enterprise Java, Web Development, and Open Source technologies, currently based in Wellington, New Zealand.
|
|