In usability we trust

UX and all things web

Adding Multiple Markers to Google Maps from JSON

Recently I’ve been receiving several emails from readers of my book, Beginning Google Maps API 3, that has a problem adding information to multiple InfoWindows when loading markers dynamically via JSON data.

In my book I have in fact described how to add multiple markers from JSON and how to attach unique InfoWindows to each of them. What I haven’t described is how to get the JSON-data to actually show up in the InfoWindows. This tutorial aims to describe how to do just that.

The JSON

In this example I’m going to use the capitals of the Scandinavian countries: Stockholm, Oslo and Copenhagen. Each capital will be displayed on the map with a marker. When you click a marker an InfoWindow will popup with some brief information about the city.

The JSON data looks like this:

var json = [
  {
    "title": "Stockholm",
    "lat": 59.3,
    "lng": 18.1,
    "description": "Stockholm is the capital and the largest city of Sweden and constitutes the most populated urban area in Scandinavia with a population of 2.1 million in the metropolitan area (2010)"
  },
  {
    "title": "Oslo",
    "lat": 59.9,
    "lng": 10.8,
    "description": "Oslo is a municipality, and the capital and most populous city of Norway with a metropolitan population of 1,442,318 (as of 2010)."
  },
  {
    "title": "Copenhagen",
    "lat": 55.7,
    "lng": 12.6,
    "description": "Copenhagen is the capital of Denmark and its most populous city, with a metropolitan population of 1,931,467 (as of 1 January 2012)."
  }
]

Note that the JSON data above contains the following information:

  • A Title in plain text
    We will use this information for a tooltip on the marker
  • A Latitude
    For the latitude portion of the position
  • A Longitude
    For the longitude portion of the position
  • A Description
    The content that will be displayed in the InfoWindows. I’ve included some HTML in it to illustrate that you’re not restricted to just plain text.

Note: In this example I have the JSON data in the same js file as the code, but in a real-life scenario you would probably grab it from an external source such as a back-end server or a web service.

Creating the map

First of all we need to create a map. The map will be centered over Scandinavia so that both Stockholm, Oslo and Copenhagen are visible.

// Creating a new map
var map = new google.maps.Map(document.getElementById("map"), {
  center: new google.maps.LatLng(57.9, 14.6),
  zoom: 6,
  mapTypeId: google.maps.MapTypeId.ROADMAP
});

If you feel unsure how this all works I recommend that you read Google Maps API 3 – The basics. It describes how to create a simple map.

Adding Markers

To create markers from the JSON-data we need to loop through it and extract the information for each marker.

for (var i = 0, length = json.length; i < length; i++) {
  var data = json[i],
      latLng = new google.maps.LatLng(data.lat, data.lng); 

  // Creating a marker and putting it on the map
  var marker = new google.maps.Marker({
    position: latLng,
    map: map,
    title: data.title
  });
}

This will add markers to the map and they will all have their own unique title which will show up as a tooltip when you hover over them with the mouse.

Adding InfoWindows

Ok, so now we've created a map and added some markers from JSON data. So far it hasn't been that hard, the way of doing this is pretty straight-forward. But to attach the InfoWindows properly is a different story. It's now that it starts to get tricky.

First of all We're going to create a global InfoWindow that we will reuse for all markers. This needs to be created outside of the loop. So just above the loop in the code we insert:

var infoWindow = new google.maps.InfoWindow();

This gives us an empty InfoWindow object that we can use on the map.

The next step is to attach a click event to each marker. In the code that executes we will fill the InfoWindow with the correct information and open it att the right location, pointing at the clicked marker.

At first glance it seems that you can do this the same way that you add markers:

// Attaching a click event to the current marker
google.maps.event.addListener(marker, "click", function(e) {
  infoWindow.setContent(data.description);
  infoWindow.open(map, marker);
});

The problem with this approach is that when you're trying to add more than one InfoWindow, they will all have the same content as the one in the last iteration. To prevent this from happening we need to use something called a closure.

Closures

What a closure does is to persist the data in each iteration of the loop so that it's bound to that particular marker.

One way of doing this is by encapsulating the code inside an anonymous function that is instantly executed. You need to make sure to pass the information from the current iteration to the anonymous function. In our case we're going to pass the current marker and the current data.

This is kind of a confusing concept and a bit hard to explain, so lets instead look at some code.

Implementing the closure

// Creating a closure to retain the correct data 
//Note how I pass the current data in the loop into the closure (marker, data)
(function(marker, data) {

  // Attaching a click event to the current marker
  google.maps.event.addListener(marker, "click", function(e) {
    infoWindow.setContent(data.description);
    infoWindow.open(map, marker);
  });

})(marker, data);

By passing the data to the anonymous function we make sure that it will stay there. We're essentially creating 3 self contained objects (closures) from our 3 locations.

Now if we're try the code, it will work perfectly and each InfoWindow will display the right information.

Tip: Nicholas Johnson explains how closures work in 5 easy bullet points in his short post, What is it with Closure?

Live Demo

Conclusion

I hope that this example will clear out some confusion on this topic. Closures are a pretty confusing concept but once you understand how it works, it's something you we'll easily be able to apply in your own code. Anyway, you should be able to grab the code from this example and use it as a boilerplate to tweak your own needs.

Happy Coding!

My Google Maps book

If you found this article useful you might be interested in my book Beginning Google Maps API 3. It covers everything you need to know to create awesome maps on your web sites. Buy it on Amazon.com

27 Comments

  1. The closure does not work for me. I see three markers but only Copenhagen displays an infoWindow. Can you please post the entire code as one page?

  2. I’m copy your knowledge.
    thanks for info.
    this very helping for me.
    thanks a lot.
    :)

  3. Seamus Campbell

    April 20, 2012 at 9:20 am

    Hi – I’m working through your book (which is great btw), and trying to use the above example. However my base code is ColdFusion. I have a query from my database which I can convert to a JSON object with serializeJSON. However my format is very different from your example and I can’t see how to loop over the json object I have.
    Is there a quick way you can see that I can read it or is there another way I should go.
    My json looks like this:
    {“COLUMNS”:[“BUSINESS_NAME”,”LATITUDE”,”LONGITUDE”],”DATA”:[[“sdfdsf Books”,-36.890408,121.154751],[“dsdsdsd Booksellers”,-14.921735,18.602189],[“fff ints”,-17.8451823,195.0541819]]}
    Any help gratefully accepted

  4. Thanks for the elaboration/clarification. I bought your book last year and highly recommend it!

  5. Thank you! The closure portion was all that I was missing for my feature to work. :)

  6. I bought your book. I want to know how to implement this same feature to read a json file instead of what is currently there. Regards.

  7. that’s the SOLUTION !!!
    thanks

  8. but what if I want one info window open on page load?
    I have tried to make a infoWindow on page load and it works, but when I click on the markers in the json data:
    a) infoWindow at page load stays open
    b) once closed it will not display again (that’ maybe because it only display on pageload)
    how can I solve this?

  9. Junaid Naseem

    May 24, 2012 at 9:59 am

    I have used code above and ‘infowindow’ is not working with Stockholm and Oslo… I can only see an infowindow after clicking Copenhagen. Could you please let me know that what could be wrong at my end.

  10. Thanks for posting this article. It’s been very helpful. I’ve managed to get things to work. Now, I’m trying to understand why it worked. I’m a bit unclear about the encapsulated code in the anonymous function:

    1 (function(marker, data) {
    2
    3 // Attaching a click event to the current marker
    4 google.maps.event.addListener(marker, “click”, function(e) {
    5 infoWindow.setContent(data.description);
    6 infoWindow.open(map, marker);
    7 });
    8
    9 })(marker, data);

    I’d been thinking that the first “(marker, data)” (ie. at line 1) was the passing in of the parameters, but I’m guessing that the parameters are actually being passed in at line 9. Is that correct? If so, then could you explain the purpose of specifying (marker, data) after the function keyword in line 1?

    Cheers!

  11. Very HAPPY right now!!!!

    June 8, 2012 at 1:54 pm

    wow… I was struggling with this for like hours. Thank you for this post.

  12. awesome

  13. John Charalambides

    July 13, 2012 at 10:06 am

    The page with the JS cannot be found, please can you show me the source as only the one marker displays the infobox.
    Thank you for a good article

  14. Hi, with your article (and of course your book) I had been able to implement more markers on a map.
    What about adding differnt icons to different markers in a Json array?

  15. You saved my life :)

    I’ve been struggling for a week trying to integrate JSON data with OverlappingMarkerSpiderfyer and closure was the only way I could get it to work..

    thanks again (and heres my code incase anyone else is struggling)

    (function() {
    window.onload = function() {
    // Creating a new map var map = new google.maps.Map(document.getElementById(“map_canvas”), { center: new google.maps.LatLng(54.6, -3.0), zoom: 6, mapTypeId: google.maps.MapTypeId.ROADMAP });

    //load data in via json var json = (function () { var json = null; $.ajax({ ‘async': false, ‘global': false, ‘url': “yourjson.json”, ‘dataType': “json”, ‘success': function (data) { json = data; } }); return json;})();

    // Creating a global infoWindow object that will be reused by all markers var infoWindow = new google.maps.InfoWindow(); var oms = new OverlappingMarkerSpiderfier(map,{markersWontMove: true, markersWontHide: true}); var usualColor = ‘eebb22′; var spiderfiedColor = ‘ffee22′; var iconWithColor = function(color) { return ‘http://chart.googleapis.com/chart?chst=d_map_xpin_letter&chld=pin|+|’ + color + ‘|000000|ffff00′; } var shadow = new google.maps.MarkerImage( ‘https://www.google.com/intl/en_ALL/mapfiles/shadow50.png’, new google.maps.Size(37, 34), // size – for sprite clipping new google.maps.Point(0, 0), // origin – ditto new google.maps.Point(10, 34) // anchor – where to meet map location );
    // Looping through the JSON data var bounds = new google.maps.LatLngBounds(); for (var i = 0, length = json.length; i < length; i++) { var data = json[i], latLng = new google.maps.LatLng(data.latitude, data.longitude); bounds.extend(latLng); // Creating a marker and putting it on the map var marker = new google.maps.Marker({ position: latLng, map: map, icon: iconWithColor(usualColor), shadow: shadow, title: data.fullname }); marker.desc = '
    '+data.comment+'';

    // Creating a closure to retain the correct data, notice how I pass the current data in the loop into the closure (marker, data) (function(marker, data) { oms.addListener('click', function(marker) { infoWindow.setContent(marker.desc); infoWindow.open(map, marker); }); oms.addListener('spiderfy', function(markers) { for(var i = 0; i < markers.length; i ++) { markers[i].setIcon(iconWithColor(spiderfiedColor)); markers[i].setShadow(null); } infoWindow.close(); }); oms.addListener('unspiderfy', function(markers) { for(var i = 0; i < markers.length; i ++) { markers[i].setIcon(iconWithColor(usualColor)); markers[i].setShadow(shadow); } }); oms.addMarker(marker);
    // Attaching a click event to the current marker /*google.maps.event.addListener(marker, "click", function(e) { infoWindow.setContent(data.comment); infoWindow.open(map, marker); });*/

    })(marker, data);map.fitBounds(bounds);
    }
    }
    })();

  16. plz povide dynamically it should be take not statically………

  17. Deepan Ayyaswamy

    January 16, 2013 at 10:47 am

    Thank u so much , it is a nice awesome tutorial

  18. is it possible to add the json data that google maps customize app provides?

  19. Thank You!
    I was stuck on the whole “closure” thing for two hours!

  20. THANK YOU! Your instructions fixed my problem so quickly!

  21. very very happy

  22. How can I do this with infobox instead of info windows

  23. Vaishnavi Desai

    June 17, 2013 at 12:28 am

    Hi, great post!
    I am trying to do something similar, but want to load the data from the JSON file, and than use it to display as a marker on the map. Could you please guide what I should be doing?

    This is the code i am using below to read from json file:

    $(document).ready(function() {

    $.getJSON(“markers.json”, function(json1) {

    $.each(json1, function(key, markers) {

    var latLng = new google.maps.LatLng(markers.lat, markers.lng);

    // Creating a marker and putting it on the map

    var marker = new google.maps.Marker({

    position: latLng,

    title: markers.title

    });

    marker.setMap(map);

    });

    });

    });

    My json file looks like:

    var markers = [

    {“lat”:-33.890542, “lng”:151.274856, “title”: “hello”},{“lat”:-33.923036, “lng”:151.259052, “title”: “hi”}];

    Could you please help me with this? I will really appreciate it. Thanks. Hoping to hear back from you.

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>