Converting Google Maps Tile Coordinates to Boundary Box Coords

Usually working with the Google Maps API is a very simple and well documented experience. Saying this though, recently we wanted to overlay an image over the map. The overlay was relevant to the exact point which you were looking at, so the coordinates had to be exact. The images were also being retrieved from an external service, which we didn’t have access to change.

The problem we were having stemmed from the fact that the Google Maps JavaScript library would give us the coordinates of where the user was looking as a long lat, but only as the corners of the viewport (outer square) – it wouldn’t give any indication as to the long lat coordinates of each tile. To overlay an image though we needed to specify the long lat of the corners of the tile.

After much struggling we finally achieved it with this function.

function tileCoordsToBBox(map, coord, zoom, tileWidth, tileHeight) {
    var proj = map.getProjection();

    // scale is because the number of tiles shown at each zoom level double.
    var scale = Math.pow(2, zoom);

    // A point is created for the north-east and south-west corners, calculated
    // by taking the tile coord and multiplying it by the tile's width and the map's scale.
    var ne = proj.fromPointToLatLng(new google.maps.Point( (coord.x+1) * tileWidth / scale, coord.y * tileHeight / scale));
    var sw = proj.fromPointToLatLng(new google.maps.Point( coord.x * tileWidth / scale, (coord.y+1) * tileHeight / scale));

    return [
        sw.lng(),
        sw.lat(),
        ne.lng(),
        ne.lat()
    ];
}

We then used this function like this:

var tileWidth = 512,
    tileHeight = 512;

var map = new google.maps.Map(document.getElementById('map-canvas'), {});

var imageMapType = new google.maps.ImageMapType({
    getTileUrl: function (coord, zoom) {
        if (zoom >= 18) {

            // The call to our earlier function
            var bbox = tileCoordsToBBox(map, coord, zoom, tileWidth, tileHeight);

            // The server endpoint for getting the images, where we pass bbox.join(',') through
            var url = "";
            return url;
        }
    },
    tileSize: new google.maps.Size(tileWidth, tileHeight),
    opacity: 0.4
});

map.overlayMapTypes.push(imageMapType);

We were helped out by this answer on StackOverflow, which was one of the few places which described out problem.