Duncan’s blog

November 4, 2011

Google Maps API – adding infowindows in response to user clicks

In response to my previous post about adding infowindows with the Google Maps API, Frank B asked if it was possible “to write a script where the user clicks on the map, and an infowindow pops up in that location containing information about that place, say the county it belongs to”. Here’s a quick something I rustled up in response to that.

Firstly, let’s just open an infowindow on a map in response to user clicks on the map.

<script type="text/javascript">
function initialize() {
	var myOptions = {
		zoom: 10,
		center: new google.maps.LatLng(50.820645,-0.137376),
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	
	var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
	
	var infowindow =  new google.maps.InfoWindow({
		content: 'Hello World!'
	});

	google.maps.event.addListener(map, 'click', function(event) {
		infowindow.setPosition(event.latLng);
		infowindow.open(map);
	}); 
}

google.maps.event.addDomListener(window, 'load', initialize);
</script>

So we simply have an event listener for any click events on the map itself. We can then find out the position of that click using event.latLng, which we can then pass to the setPosition function on the infowindow. The final .open() simply shows the infowindow; if it’s already open, this won’t do anything (but it causes no harm to call it again).

This opens the same infowindow anywhere we click on the map:

Now, let’s get some content about each location we click on, and use that to dynamically set the content of the infowindow. To do this we need to do a reverse Geocoding request (i.e. getting address details from a latlng coordinates). The usual use of the Geocoding service is to do the opposite – find out the coordinates of an address.

Interestingly Google’s API reference says to use a ‘location’ parameter but their Developer documentation says to use a ‘latLng’ parameter. Both seemed to work for me, but I’d say the API reference should be considered the correct source in general.

<script type="text/javascript">
var map, geocoder, infowindow;
	
function initialize() {
	var myOptions = {
		zoom: 10,
		center: new google.maps.LatLng(50.820645,-0.137376),
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	
	map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
	
	geocoder = new google.maps.Geocoder();
	
	infowindow =  new google.maps.InfoWindow({
		content: ''
	});

	google.maps.event.addListener(map, 'click', function(event) {
		doGeocoding(event.latLng);
	});
}

function doGeocoding(latLng) {	
	geocoder.geocode({'location': latLng}, function(results, status) {
		if (status == google.maps.GeocoderStatus.OK) {
			if (results[1]) {	// use 2nd array item, a less-specific address
				infowindow.setContent(results[1].formatted_address);
			} else {
				infowindow.setContent('');
			}
		} else {
			infowindow.setContent('no address found');
		}
	});
	
	infowindow.setPosition(latLng);
	infowindow.open(map);
}

google.maps.event.addDomListener(window, 'load', initialize);
</script>

This code does strange things for me when clicking on the area at sea. If it’s the first click on the map, it doesn’t display any infowindow at all. If I initially click on the land to get an address, subsequent clicks on the sea use that same address, unless I click further out from the land, in which case it gives me ‘no address’. Think it’s an issue with trying to do setContent(”). I’m publishing this article despite it not being quite code-complete, in the hope it helps Frank (and I’m happy to take suggestions on how to get it fully working as it should).

October 8, 2011

Google Maps API – infowindows

So previously I showed how to create a basic map, then how to add markers to it. Now suppose you’re wanting to have one of those little bubbles pop up when you click on your map with some information in it. These are called infowindows in Google’s terminology.

Let’s start with the very basics – creating a map which has an infowindow already visible on it.

function initialize() {
	var homeLatlng = new google.maps.LatLng(51.476706,0);

	var myOptions = {
		zoom: 15,
		center: homeLatlng,
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};

	var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

	var infowindow =  new google.maps.InfoWindow({
		content: 'Hello World!',
		map: map,
		position: homeLatlng
	});
}

google.maps.event.addDomListener(window, 'load', initialize);

So we create an infowindow, specify some content for it and which map it’s for, and give it a latlong position (which coincidentally also happens to be where I’ve specified this map is to be centered, although that doesn’t have to be the case). The documentation says that “After constructing an InfoWindow, you must call open to display it on the map“; however I find the above code will open the infowindow without needing to explicitly call the open function.

But this shows the infowindow immediately. Perhaps you’d rather it only appeared after some user interaction. Typically you’d put markers on the map, and when the user clicks the marker, then you’d show the infowindow.

So let’s create the infowindow slightly differently. This time, don’t specify the position attribute. Then create a marker as normal, and finally we can open the infowindow, and we pass the marker as the second parameter. So the infowindow gets anchored to the marker’s position.

var infowindow =  new google.maps.InfoWindow({
	content: 'Hello World!',
	map: map
});

var marker = new google.maps.Marker({
	position: homeLatlng, 
	map: map
});

infowindow.open(map, marker);

So we’ve now added a marker to the map, which the infowindow is then anchored to.

You probably only want this to happen after the user has clicked on that marker. For that we need to have an event listener function. There are many of these available for all the different map elements, in this case we want to listen to the mouseclick event on that marker.

google.maps.event.addListener(marker, 'click', function() {
	infowindow.open(map, this);
});

So we say to the Google Maps event handler framework, on the marker, listen for any ‘click’ events. And when that happens, call this anonymous function which currently only has one line, to open the marker.

We could also make this infowindow appear in response to other events, e.g. if you simply hovered the mouse over the marker.

google.maps.event.addListener(marker, 'mouseover', function() {
	infowindow.open(map, this);
});

And then to make the infowindow disappear again when you move the mouse away from the marker, just call the close() function on mouseout.

google.maps.event.addListener(marker, 'mouseout', function() {
	infowindow.close();
});

Suppose you had several markers on your map, each of which would have its own infowindow. Here’s one way to deal with that. Firstly have an array of everything you need for each marker, arrDestinations. Then loop over them, adding a marker for each. We only have one infowindow variable, and we just update the content for it in response to user clicks. We need to delegate the event handler to an external function, bindInfoWindow, otherwise you end up just getting the content of the last array item for each marker.

<script type="text/javascript">
function initialize() {
	var i;
	var arrDestinations = [
		{
			lat: 50.815155, 
			lon: -0.137072, 
			title: "Brighton Pier", 
			description: "Brighton Palace Pier dates to 1899"
		},
		{
			lat: 50.822638, 
			lon: -0.137361, 
			title: "Brighton Pavilion", 
			description: "The Pavilion was built for the Prince of Wales in 1787"
		},
		{
			lat: 50.821226, 
			lon: -0.139372, 
			title: "English's", 
			description: "English's Seafood Restaurant and Oyster Bar"
		}
	];
	
	var myOptions = {
		zoom: 15,
		center: new google.maps.LatLng(50.820645,-0.137376),
		mapTypeId: google.maps.MapTypeId.ROADMAP
	};
	
	var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
	
	var infowindow =  new google.maps.InfoWindow({
		content: ''
	});
	
	// loop over our array
	for (i = 0; i < arrDestinations.length; i++) {
		// create a marker
		var marker = new google.maps.Marker({
			title: arrDestinations[i].title,
			position: new google.maps.LatLng(arrDestinations[i].lat, arrDestinations[i].lon),
			map: map
		});
		
		// add an event listener for this marker
		bindInfoWindow(marker, map, infowindow, "<p>" + arrDestinations[i].description + "</p>");  
	}
}

function bindInfoWindow(marker, map, infowindow, html) { 
	google.maps.event.addListener(marker, 'click', function() { 
		infowindow.setContent(html); 
		infowindow.open(map, marker); 
	}); 
} 

google.maps.event.addDomListener(window, 'load', initialize);
</script>

In production you’d probably use a server-side language to populate the array and just pass it into the initialize function. There are other ways of doing this as well. Another approach is to have multiple infowindows, one for each marker. On click, you close any open infowindows, and then just open the one that corresponds to the current marker. I don’t believe that approach is as efficient as my example though.

Theme: Rubric. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.