Nowadays many sites uses embedded map from Google for purposes like showing object location, drive-in way or more complex stuff. Recent Google Maps Version3 (V3) API becomes very powerful, but remained very clear and easy to use.

While developing site for real estate company I faced with necessity to display fancy, complex overlays on top of the map when user clicks on the marker.

There are some standard ways of showing so called info-windows. The problem is that it is not possible put complex formatting there or catch mouse clicks events.

To solve it I have to to dig into API internals and recent version V3 has some surprises here.

The easiest way to show custom overlay is to determine coordinates of clicked marker and position DIV over the map to right position. Sounds easy.

# API V2 code, that does not work in V3

I found some code that should do the trick, but it does not work. It was designed for V2 API.

var overlay = new google.maps.OverlayView();
overlay.draw = function() {};
overlay.setMap(map);
var proj = overlay.getProjection();
var pos = marker.getPosition();
var p = proj.fromLatLngToContainerPixel(pos);

After some API digging and googling, I found out that there are no documented ways of determiniting pixel position of GoogleMap point. Thats sad.

# API V3 code, marker to pixel position

Here is the semi-hacked code that actually does the trick

var topRight=gmap.getProjection().fromLatLngToPoint(gmap.getBounds().getNorthEast ());
var bottomLeft=gmap.getProjection().fromLatLngToPoint(gmap.getBounds().getSouthWest());
var scale=Math.pow(2,gmap.getZoom());
var worldPoint=gmap.getProjection().fromLatLngToPoint(marker.getPosition());
var point = new google.maps.Point((worldPoint.x-bottomLeft.x)*scale,(worldPoint.y-topRight.y)*scale);

Where

  • gmap - is GoogleMap object
  • marker - is GoogleMap marker object
  • point - returned object contains maker coordinates related to upper-left corner of GoogleMap area

# Overlay position on GoogleMap - Example

Below you can find working example, I made overlay window simple, but since it is DIV there is no limits of pushing it.

# Overlay position on GoogleMap - View Code

document.write('<div id="overlay" style="z-index: 1000; position: absolute;display:none;width:200px;height:100px;background-color:white;border:1px solid grey;padding:10px;">ANY CUSTOM FANCY OVERLAY HERE<br/><br/><br/><br/><br/><br/><a href="#" id="close">close</a></div>');
document.write('<div id="map" style="width:500px; height:500px;"></div>');

// Get center
var map_center = new google.maps.LatLng(50.1700235000, 4.7319823000);

// Load google map
var map = new google.maps.Map( document.getElementById("map"),  {
	zoom: 3,
	center: map_center,
	mapTypeId: google.maps.MapTypeId.HYBRID,
	panControl: false,
	streetViewControl: false,
	mapTypeControl: false
});

var pos;
var marker_list = [];
for(var i = 0 ; i < 10 ; i++) {
	pos = new google.maps.LatLng(Math.floor(Math.random()*100), Math.floor(Math.random()*50));
	marker_list.push( new google.maps.Marker({
		position: pos, 
		map: map, 
		title: 'Title',
		icon: '/other/gmap/marker.gif'
	}));
	var storyClick = new Function("event", "markerClick("+i+");");
	google.maps.event.addListener(marker_list[i], 'click', storyClick);
}

function markerClick(num) {
	var topRight=map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
	var bottomLeft=map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
    	var scale=Math.pow(2,map.getZoom());
	var worldPoint=map.getProjection().fromLatLngToPoint(marker_list[num].getPosition());
	var point = new google.maps.Point((worldPoint.x-bottomLeft.x)*scale,(worldPoint.y-topRight.y)*scale);

        jQuery("#overlay").css({
	    left:(jQuery("#map").position().left + point.x - jQuery("#overlay").width()/2),
	    top:(jQuery("#map").position().top + point.y - jQuery("#overlay").height() - 50)
	});
        jQuery("#overlay").show();
}

jQuery('#close').click(function(e) {
    e.preventDefault();
    jQuery("#overlay").hide();
});

google.maps.event.addListener(map, 'center_changed', function() {
        jQuery("#overlay").hide();
});
google.maps.event.addListener(map, 'zoom_changed', function() {
        jQuery("#overlay").hide();
});