Thoughts by Techxplorer

Thoughts on my experiences with technology

Creating dynamic InfoWindows on a Google Map with Clustered Markers

My next major task for the AusStage project is the development of a browse based interface for venue and event data. The basic idea is that on a single map all of the venues that we have coordinate information for would be listed. This allows a user the freedom to explore the map and visualise venue and event data in a new way.

Using the test database there are over 900 venues with coordinate information and this means that I needed to find some way to cluster markers to help speed up the performance of the map. Also a map with over 900 placemarks on it would be imposing to first time users. After some searching of the Interweb I decided on the use of the MarkerClusterer JavaScript library which is part of the GMaps Utility Library.

To help speed up the initial generation of the map I also decided to separate the venue information from the event information. This means that the InfoWindows, the little popups that are displayed when a user clicks on a placemark on the map, need to be generated dynamically using an AJAX based technique.

I also wanted to use the MapIconMaker library to style the placemarks to keep thelook and feel of the maps on the site consistent.

The code needed to achieve this is divided into two sections.

The first section builds the map and adds all of the placemarks to it. The MarkerClusterer library takes care of clustering the placemarks and the MapIconMaker library build the icon. The code looks like this:


// declare helper variables
var markerUrl = "url-to-venue-data";
var markerCluster;

// create a new map and centre it on australia
map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(-25.947028, 133.209639), 3);
map.setUIToDefault();

// get the marker data
GDownloadUrl(markerUrl, function(data) {

  // get an icon - in keeping with style
  var newIcon = MapIconMaker.createMarkerIcon({width: 32, height: 32, primaryColor: "#FFBC3F"});

  // store the created placemarks
  var markers = [];

  // get the xml data
  var xml = GXml.parse(data);

  // extract the markers from the xml
  var points = xml.documentElement.getElementsByTagName("marker");

  // build a group of markers
  for (var i = 0; i < points.length; i++) {

    // get the coordinates
    var latlng = new GLatLng(parseFloat(points[i].getAttribute("lat")), parseFloat(points[i].getAttribute("lng")));

    // create the marker
    var marker = new GMarker(latlng, {icon: newIcon});

    // build the url for the info
    var url = "url-to-event-data" + points[i].getAttribute("id");

    // get a function to respond to the click
    var fn = markerClick(url, latlng);

    // add an event listener to listen for the click on a marker
    GEvent.addListener(marker, "click", fn);

    // add the marker to the list
    markers.push(marker);
  }

  // add the markers to the map
  markerCluster = new MarkerClusterer(map, markers);
});

The key lines are:

  • Line 11: Where the venue data is downloaded using the GDownloadUrl function
  • Line 14: Where the icon style for the placemarks is defined
  • Lines 19 – 23: Where the XML data is processed
  • Lines 26 – 45: Where the XML is used to create a group of markers
  • Line 48: Where the MarkerClusterer library is used to manage the custering of the placemarks on the map

The key to achieving the dynamically loading InfoWindows is line 38. This line uses the markerClick function, the second part of the strategy, to construct another function which will respond to the event when a user clicks on a placemark.

The markerClick function looks like this:

// function to build a function to respond to the click on a marker
function markerClick(url, latlng) {
  return function() {
    // download the event information
    GDownloadUrl(url, function(html) {
      // open an info window with the information
      map.openInfoWindowHtml(latlng, html, {maxWidth:450, maxHeight:400, autoScroll:true});
    });
  }
}

This function again uses the GDownloadUrl function to download data, this time a HTML snippet that lists all events associated with a venue. The data that is retrieved is then used to populate an InfoWindow that is opened using the openInfoWindowHtml function associated with the main map object. By using the same GLatLng object as that used to construct the placemark the InfoWindow appears in the right spot, over the placemark that was clicked.

Dynamically retrieving the content that is used to populate the InfoWindow introduces a slight delay to the display of the InfoWindow as the HTML content must be displayed. A planned refinement of this technique is to display some sort of loading message to the user to keep them informed of what is happening.

A pre-alpha version of the map, when it is first generated looks like this:

Map with clustered markers.
(Click for larger version)

Clicking one of the clustered markers zoom in the map and expands the cluster of markers until such time as single makers are displayed. For example in the example below the map has been zoomed into Adelaide by clicking the clustered marker at this area.

Map with clustered markers - zoomed into Adelaide. (Click for larger version)

Map with clustered markers, zoomed in on Adelaide.
(Click for larger version)

One of the benefits of the markers created by the MarkerClusterer library is that the clusters are automatically numbered to show how many individual markers are contained in the cluster. Clicking an individual marker, there are four on this map, brings up an InfoWindow displaying information in a similar way to that which I’ve outlined before on this blog.

Category: Thoughts