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

About the author

Gabriel Svennerberg is an UX Designer and FrontEnd Developer living in Sweden. When he's not busy designing or building applications at Meetod, he writes about UX Design and all things web on In usability we trust. Gabriel is the author of the book Beginning Google Maps API 3 published by Apress. Gabriel on Google+
  • Corey Bell

    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?

    • http://www.svennerberg.com Gabriel Svennerberg

      You can always check out the code in the Live Demo. Here’s a direct link to the js file.

      Cheers!

      • Corey Bell

        Thank you! I got it to work, I think I had the order mixed up.

  • http://www.facebook.com/reza.esa1 Reza Esa

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

  • Seamus Campbell

    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

  • Mike Zin

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

    • http://www.svennerberg.com Gabriel Svennerberg

      That’s nice to hear. Thanks for the recommendation!

  • http://twitter.com/riemino riemino

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

  • Batowiise

    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.

  • Pippo_mares

    that’s the SOLUTION !!!
    thanks

  • Pippo mares

    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?

  • Junaid Naseem

    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.

  • BigusJones

    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!

  • Very HAPPY right now!!!!

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

  • Liuyao010

    awesome

  • John Charalambides

    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

    • http://www.svennerberg.com Gabriel Svennerberg

      Sorry about that, now it’s up again. (I’ve been on vacation for the last 3 weeks, hence my late reply)

  • Marino

    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?

  • Gary Baldwin

    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);
    }
    }
    })();

  • varaprasad

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

  • Pingback: Anyone using the new version of Mapstraction? | Marion Walton

  • Deepan Ayyaswamy

    Thank u so much , it is a nice awesome tutorial

  • http://www.facebook.com/cristi.jurcan Cristi Jurcan

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

  • http://www.facebook.com/bob.langdon3 Bob Langdon

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

  • Pingback: Great Expectations

  • Alison

    THANK YOU! Your instructions fixed my problem so quickly!

  • mansee

    very very happy

  • joaquin

    How can I do this with infobox instead of info windows

  • Vaishnavi Desai

    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.

  • Pingback: Google Maps v3 -> Database/mysql -> AJAX -> LOOP -> AJAX -> Loop | BlogoSfera