Google map api v3, troubles setting mutiple markers using checkboxes

Hey guys, I was using a google map V2 API, but decided as it is deprectaed, to step up and use the latest version, and it is doing my head in. Basically all I want is a map, that has 5 checkboxes, checkbox1, checkbox2 etc… and if one or all is checked then a marker will appear on the map that I have location preset, the marker when clicked will show a little information and contain a url. I was able to get the map with the five markers completed using V2, but when I was researching how to use checkboxes, i kept being led to the V3 API. I think the Google documentation is pretty bad for this PAI, as there is tons and tons of information but none of it is explaing the simple things, like how to set mutilple markers in V3. It will instead show you how to create polygons using images and fancy stuff that is not essential.

I appreciate anyones advice on this as I am struggliung to find straight forward expertise on this subject.

Cheers!!!

Here’s a demo that seems to cover what you want to achieve.
Google Maps Javascript API v3 Example: Marker Categories

Paul many thanks for that example, that is exactly what I am looking for!! The trouble is, I am analyzing the source code, I cannot see where all the markers get their longitude and latitude postions from, am I missing something? I have messed about with it for 20 minutres, I cannot for the life of me find the code.

Thanks again for your help!!!

Look at lines 190-193 of the source code.


// Read the data
downloadUrl("categories.xml", function(doc) {
    var xml = xmlParse(doc);
    var markers = xml.documentElement.getElementsByTagName("marker");

Ah, that is totally different compared to the way I was doing it with the V2 maps:


		for(i=0; i<markers.length; i++)
		{
			addMarker(markers[i].latitude, markers[i].longitude, markers[i].name);
		}
      }
    }
var markers = [
	{
		'latitude': 60.1698,
		'longitude': 24.9382 ,
		'name': '<strong>Children of Bodom</strong> <br/>Helsinki, Finland<br/><a href="http://www.cobhc.com/" target ="_blank">Children of bodom website</a>',

	},
	{
		'latitude':  30.2672,
		'longitude': -97.7431 ,
		'name': '<strong>Pantera</strong> <br/>Austin, Texas<br/><a href="http://www.pantera.com/" target ="_blank">Pantera website</a>',

	},
	{
		'latitude': 57.6970,
		'longitude':  11.9865 ,
		'name': '<strong>In Flames</strong> <br/>Gothenburg, Sweden<br/><a href="http://www.inflames.com/" target ="_blank">In Flames website</a>',

	},
	{
		'latitude':  37.8044,
		'longitude': -122.2711  ,
		'name': '<strong>Machine Head</strong> <br/>Oakland, California<br/><a href="http://www.machinehead1.com/" target ="_blank">Machine Head website</a>',

	},
	{
		'latitude':  52.5234,
		'longitude': 13.4114  ,
		'name': '<strong>Rammstein</strong> <br/>Berlin, Germany<br/><a href="http://www.rammstein.de/en/" target ="_blank">Rammstein website</a>',

	}
	];
	function addMarker(latitude,longitude,description,type)
	{
		var marker = new GMarker(new GLatLng(latitude,longitude));
		
		GEvent.addListener(marker, 'click',
		function(){
			marker.openInfoWindowHtml(description);
		}
		);
	map.addOverlay(marker);	
	}

It is always the same with javascript so many ways to do so many things. I will look into the method used in V3 and see if I can figure out how to do it.

Many thanks again for helping me out!

Both his way of retrieving the data from an XML file, and your way of storing the data in an associated list, both end up with the same result, of a marker array containing the data.

There’s no need for you to change the way your data is stored.

Thanks Paul. I am curious as to how it was done using an XML file, I can’t actually get his code to work with my own coordinates because I am too fresh at javascript to fully understand how it works, I am not sure how to use his code with my way of storing the data. Basically I am not sure what part does what when it comes to accessing stored coordinates.

Thanks again!!

Well, he passes the info to a createMarker function:


function createMarker(latlng,name,html,category) {
    ...
}

Where does he get the latlng from? That’s in the code immediately after the markers are read in.


// Read the data
downloadUrl("categories.xml", function(doc) {
var xml = xmlParse(doc);
var markers = xml.documentElement.getElementsByTagName("marker");

for (var i = 0; i < markers.length; i++) {
    // obtain the attribues of each marker
    var lat = parseFloat(markers[i].getAttribute("lat"));
    var lng = parseFloat(markers[i].getAttribute("lng"));
    var point = new google.maps.LatLng(lat,lng);
    var address = markers[i].getAttribute("address");
    var name = markers[i].getAttribute("name");
    var html = "<b>"+name+"<\\/b><p>"+address;
    var category = markers[i].getAttribute("category");
    // create the marker
    var marker = createMarker(point,name,html,category);
}

Are you having trouble find these parts of the code when you look at his demo page?

Hi Paul thanks for your code breakdown!

I can see all this in the code when I look at teh source code, but it was easier to see where when things were happeneing when you pointed the sections out. I spent some time again trying to merge my way of storing data with his map code, but the difficulty I am having is that I don’t know how to alter the code, trust me I have spent a lot of time experimenting.

So basically I want to have my own info passed:

function createMarker(latlng,name,info,url) {
    ...
}

So the original way I had my code was a set of variables:


var markers = [
	{
		'latitude': 60.1698,
		'longitude': 24.9382 ,
		'name': '<strong>Children of Bodom</strong> <br/>Helsinki, Finland<br/><a href="http://www.cobhc.com/" target ="_blank">Children of bodom website</a>',

	},
	{
		'latitude':  30.2672,
		'longitude': -97.7431 ,
		'name': '<strong>Pantera</strong> <br/>Austin, Texas<br/><a href="http://www.pantera.com/" target ="_blank">Pantera website</a>',

	},
	{
		'latitude': 57.6970,
		'longitude':  11.9865 ,
		'name': '<strong>In Flames</strong> <br/>Gothenburg, Sweden<br/><a href="http://www.inflames.com/" target ="_blank">In Flames website</a>',

	},
	{
		'latitude':  37.8044,
		'longitude': -122.2711  ,
		'name': '<strong>Machine Head</strong> <br/>Oakland, California<br/><a href="http://www.machinehead1.com/" target ="_blank">Machine Head website</a>',

	},
	{
		'latitude':  52.5234,
		'longitude': 13.4114  ,
		'name': '<strong>Rammstein</strong> <br/>Berlin, Germany<br/><a href="http://www.rammstein.de/en/" target ="_blank">Rammstein website</a>',

	}
	];

With a simple fucntion to add them:

	function addMarker(latitude,longitude,description,type)
	{
		var marker = new GMarker(new GLatLng(latitude,longitude));
		
		GEvent.addListener(marker, 'click',
		function(){
			marker.openInfoWindowHtml(description);
		}
		);
	map.addOverlay(marker);	
	}

My problem is getting all this together, I am really struggling and I have spent way too much time trying to do this, which is obviously too advanced for me :frowning:

I suppose, it’s all part of the learning experience with javascript.

Thanks for all your assistance Paul.

Well if it helps, the V2 example categories page is available too. Would it help if you were to compare and contrast the techniques use there, with the [url=“http://www.geocodezip.com/v3_MW_example_categories.html”]V3 example categories page?

Thanks Paul I will look into the links you posted, I see there is tutorial from this guy and perhaps I can learn from this. It is mentioned in the tutorial that the map api is not an easy subject, so perhaps this is why I am struggling. I won’t give up trying to achieve the map I want!
:slight_smile:

Thanks!

Paul that is a lot better with the tutorials, I have been working on the code provided but I am having one little issue. I decided I want to use an xml file but the html links appear next to the map but when I click the links nothing shows on the map as it should like in the example I am copying here:

Google Maps

My map looks the same but as I said when I click on the links nothing shows in the map. I have the xml file copied from the source so it is definitely correct. The main difference with my page and the example is that I have the javasript in an external j.s file and am calling the jascript by using the body tag:

<body onload="initialize()" >

Here is my web page whichI am almost finished, but just having this one issue:

http://0902221.studentweb.abertay.ac.uk/map.html

I appreciate your time with this, if ity wasn’t for your help Paul I would have never gotten this far.

Many thanks!

The link run the myclick function, which triggers the marker from an array called gmarker


function myclick(i) {
    GEvent.trigger(gmarkers[i], "click");
}

Due of that, he’s had to make sure that gmarkers is a globally accessible file


// arrays to hold copies of the markers used by the side_bar
// because the function closure trick doesnt work there
var gmarkers = [];

That was defined outside of any function, so that it becomes globally accessible.
Your code differs from that because you have put it in a function called initialize.

You can fix things up by making it explicit that gmarkers has to be a global variable. Change the gmarkers references to window.gmarkers


window.gmarkers = [];
...
window.gmarkers.push(marker);
...
...(window.gmarkers.length-1)...
...
GEvent.trigger(window.gmarkers[i], "click");

That should get things working.

Later on if you want to, you can use some fancier coding to place that array as a property on the map itself. But that can be for later.

Paul, thank you very much! I changed everything as you suggested, it still wouldn’t work but as you mentioned the function myclick was originall not in another frucntion I moved it outside of fucntion initialize and it worked! :slight_smile:

Hopefully now I can build from this and create the map I wanted! Thanks again for all your help on this! Javascript is definitely very tough, I feel I need to read a book on it!

You have been a great help with this, cheers!!!

Not just one book, but multiple books and further learning from there.

Despite initial impressions, JavaScript is not a toy language.

I will definitely be getting a recent book and starting there!

I have one more question for you Paul, I have started creating the map I want, two check boxes, one bands, the other members, I have adapted the code so that I should have links that open markers, and the links are only displayed if the checkbox is ticked. Can you look at my code and see how I am not able to get the checkbox thing to work please?

My page

The javascript:

function initialize() {
   if (GBrowserIsCompatible()) {
      window.gmarkers = [];
      var gicons = [];

      var baseIcon = new GIcon(G_DEFAULT_ICON);
      baseIcon.iconAnchor = new GPoint(9,34);
      baseIcon.iconSize = new GSize(20,34);
      baseIcon.infoWindowAnchor = new GPoint(9,2);


      gicons["bands"] = new GIcon(baseIcon,"colour086.png");
      gicons["members"] = new GIcon(baseIcon,"colour108.png");


      // A function to create the marker and set up the event window
      function createMarker(point,name,html,category) {
        var marker = new GMarker(point,gicons[category]);
        // === Store the category and name info as a marker properties ===
        marker.mycategory = category;
        marker.myname = name;
        GEvent.addListener(marker, "click", function() {
          marker.openInfoWindowHtml(html);
        });
        window.gmarkers.push(marker);
        return marker;
      }

      // == shows all markers of a particular category, and ensures the checkbox is checked ==
      function show(category) {
        for (var i=0; i<window.gmarkers.length; i++) {
          if (window.gmarkers[i].mycategory == category) {
            window.gmarkers[i].show();
          }
        }
        // == check the checkbox ==
        document.getElementById(category+"box").checked = true;
      }

      // == hides all markers of a particular category, and ensures the checkbox is cleared ==
      function hide(category) {
        for (var i=0; i<window.gmarkers.length; i++) {
          if (window.gmarkers[i].mycategory == category) {
            window.gmarkers[i].hide();
          }
        }
        // == clear the checkbox ==
        document.getElementById(category+"box").checked = false;
        // == close the info window, in case its open on a marker that we just hid
        map.closeInfoWindow();
      }

      // == a checkbox has been clicked ==
      function boxclick(box,category) {
        if (box.checked) {
          show(category);
        } else {
          hide(category);
        }
        // == rebuild the side bar
        makeSidebar();
      }



      // == rebuilds the sidebar to match the markers currently displayed ==
      function makeSidebar() {
        var html = "";
        for (var i=0; i<window.gmarkers.length; i++) {
          if (!window.gmarkers[i].isHidden()) {
            html += '<a href="javascript:myclick(' + i + ')">' + window.gmarkers[i].myname + '<\\/a><br>';
          }
        }
        document.getElementById("side_bar").innerHTML = html;
      }


      // create the map
      var map = new GMap2(document.getElementById("map"));
      map.addControl(new GLargeMapControl());
      map.addControl(new GMapTypeControl());
      map.setCenter(new GLatLng(0.7465,-39.4629), 2);


      // Read the data
      GDownloadUrl("categories.xml", function(doc) {
        var xmlDoc = GXml.parse(doc);
        var markers = xmlDoc.documentElement.getElementsByTagName("marker");

        for (var i = 0; i < markers.length; i++) {
          // obtain the attribues of each marker
          var lat = parseFloat(markers[i].getAttribute("lat"));
          var lng = parseFloat(markers[i].getAttribute("lng"));
          var point = new GLatLng(lat,lng);
          var address = markers[i].getAttribute("address");
          var name = markers[i].getAttribute("name");
          var html = "<b>"+name+"<\\/b><p>"+address;
          var category = markers[i].getAttribute("category");
          // create the marker
          var marker = createMarker(point,name,html,category);
          map.addOverlay(marker);
        }

        // == show or hide the categories initially ==
        show("bands");
        hide("members");

        // == create the initial sidebar ==
        makeSidebar();
      });
    }

    else {
      alert("Sorry, the Google Maps API is not compatible with this browser");
    }

}

  function myclick(i) {
        GEvent.trigger(window.gmarkers[i],"click");
      }

The xml file I have:

<markers>
  <!-- Dont use copy and paste on this XML file, use "View Source" or "Save As"
       What the browser displays is *interpreted* XML, not XML source. -->
 <marker lat="30.2672" lng="-97.7431"
 name="pantera"
   html='Some stuff to display in the<br>First Info Window <br>With a <a href="http://www.pantera.com">Link</a> to my home page'
   category="bands"
   label="Marker One"
 />
 <marker lat="60.1698" lng="24.9382 "
  name="Rammstein"
  html='Some stuff to display in the<br>Second Info Window with an image<br><img src="Images/fb.jpg" width=150 height=100>'
   category="bands"
  label="Marker Two"
 />
 <marker lat="43.82589" lng="-79.10040"
  name="COBHC"
  html='<h3>All the usual html formatting commands work</h3>'
  category="members"
  label="Marker Three"
 />
 <marker lat="43.9" lng="-79.5"
  name="IN Flames"
  html='<div style="background-color:#FFFF88; font-family:cursive;" border:solid 3px black;">You can use a div with<br>style settings.<br>Avoid using width settings<br>when using nowrap</div>'
  category="members"
  label="Marker Four"
 />
</markers> 

Also there seems to be only two links appearing in the sidebar when there should be four, I may have set the category name wrong in the xml, but I just looked through all the parameters that the javascript asks for and tried to put that in the xml per each marker.

Sorry to keep asking for help with this, I do try to do it by myself before asking.

Thanks again!

The checkbox tries to run a function called boxclick, but when it’s attempted the function doesn’t appear to be available.

That’s because the boxclick is defined from inside of the initialize function, so the boxclick function is only available to anything that’s running from within the scope of that initialize function.

As for a solution, it’s clear that events that are defined as an inline event attribute demand global access to function, which is not a good idea.

Remove the inline event on those checkboxes so that they are now just:


<input type="checkbox" id="bandsbox">
<input type="checkbox" id="membersbox">

and attach the event from within the initialize function instead. Just after the definition of the boxclick function might be a good place.


document.getElementById('bandsbox').onclick = function () {
    boxclick(this, 'bands');
}
document.getElementById('membersbox').onclick = function () {
    boxclick(this, 'members');
}

Once the onclick event is being assigned by scripting, the function will have easy access to the element that fired the event via the this keyword.

What that means for you is that you now don’t need to pass this as the first argument of the function any more.


function boxclick([s][color="red"]box, [/color][/s]category) {
    [color="green"]var box = this;[/color]
    if (box.checked) {
        ...
}
document.getElementById('bandsbox').onclick = function () {
    boxclick([s][color="red"]this, [/color][/s]'bands');
};
document.getElementById('membersbox').onclick = function () {
    boxclick([s][color="red"]this, [/color][/s]'members');
};

Taking things even further, it’s even now possible to not pass the category either, because the function could take from “membersbox” from the id, and rerieve from it all but the last three characters:


function boxclick([s][color="red"]category[/color][/s]) {
    var box = this;
    [color="green"]var category = box.id.substr(0, box.id.length - 3);[/color]
    if (box.checked) {
        ...
}
document.getElementById('bandsbox').onclick = [s][color="red"]function () {
    [/color][/s]boxclick[s][color="red"]('bands');
}[/color][/s];
document.getElementById('membersbox').onclick = [s][color="red"]function () {
    boxclick[s][color="red"]([s][color="red"]this, [/color][/s]'members');
}[/color][/s];

That way you end up with:


function boxclick() {
    var box = this;
    var category = box.id.substr(0, box.id.length - 3);
    if (box.checked) {
        ...
}
document.getElementById('bandsbox').onclick = boxclick;
document.getElementById('membersbox').onclick = boxclick;

Paul you are a genuis!

I never saw your last two posts by mistake so I spent an hour this morning working on what you said about the scope and the functions within, I then saw the last two posts and then used all the information you gave me and now it is working, finally! Thank god and thank you!

This has been the most difficult thing I have done in javascript since starting it a few weeks ago, I thought form validation was difficult!

I have learnt a lot from trying to do this, such as functions inside functions and how they are put into action. Parameters are beconing clearer to me and I am getting used to what is what in the code by looking at it!

I do have one more small question, I am about to create all my markers in the xml file, but I notice that I am having a problem getting a hyperlink to display in the marker information window, the xml has a marker tag and everything is inside it, everything works except the html part, do you know why it does not work Paul?

 <marker lat="30.2672" lng="-97.7431"
 name="pantera"
 address="14 avenue"
  html='<div style="background-color:#FFFF88; font-family:cursive;" border:solid 3px black;">You can use a div with<br>style settings.<br>Avoid using width settings<br>when using nowrap</div>'
   category="bands"
   label="Marker One"
 />

I can’t thank you enough for all the help you have provided, I can’t even see a thanks button or I would press it a hundred times! :slight_smile:

Can I ask, how long have you been using javascript, and do you recommend that I concentrate on this language, or will it be replaced in the near the future?

Thanka again!