Based on http://bl.ocks.org/mbostock/899711 which uses D3 to draw elements into a google maps overlay layer, but doesn’t use d3.geo machinery to draw map geometry. This gist illustrates how to align a D3 mercator projection with google maps so we can do standard d3 mapping stuff on top of the google API.
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<script type="text/javascript" src="//maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="//d3js.org/queue.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<style type="text/css">
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#map {
margin: 0;
padding: 0;
}
#d3map {
/*pointer-events: none;*/
-webkit-transform: none;
position: relative;
top: 0px;
left: 0px;
}
#countries path {
stroke-width: 1px;
vector-effect: non-scaling-stroke;
fill: none;
stroke: #333;
}
</style>
</head>
<body>
<script type="text/javascript">
var width = 600,
height = 400,
div = d3.select('body')
.append('div')
.attr('id','map')
.style('width', width + 'px')
.style('height', height + 'px');
// Create the Google Map…
var map = new google.maps.Map(div.node(), {
zoom: 2,
center: new google.maps.LatLng(37.76487, -122.41948),
mapTypeId: google.maps.MapTypeId.TERRAIN,
minZoom: 2 // stuff goes wrong if we allow world wraparound
});
// Load the data. When the data comes back, create an overlay.
queue()
.defer(d3.json, "world-110m.json")
.await(ready);
var svg, overlay;
function ready(error, world) {
var countries = topojson.feature(world, world.objects.countries).features,
land = topojson.feature(world, world.objects.land);
overlay = new google.maps.OverlayView();
overlay.onAdd = function() {
// create an SVG over top of it.
svg = d3.select(overlay.getPanes().overlayLayer)
.append('div')
.attr('id','d3map')
.style('width', width + 'px')
.style('height', height + 'px')
.append('svg')
.attr('width', width)
.attr('height', height);
svg.append('g')
.attr('id','countries')
.selectAll('path')
.data(countries)
.enter().append('path')
.attr('class','country');
overlay.draw = redraw;
google.maps.event.addListener(map, 'bounds_changed', redraw);
google.maps.event.addListener(map, 'center_changed', redraw);
};
overlay.setMap(map);
}
function redraw() {
var bounds = map.getBounds(),
ne = bounds.getNorthEast(),
sw = bounds.getSouthWest(),
projection = d3.geo.mercator()
.rotate([-bounds.getCenter().lng(),0])
.translate([0,0])
//.center([0,0])
.scale(1),
path = d3.geo.path()
.projection(projection);
var p1 = projection([ne.lng(),ne.lat()]),
p2 = projection([sw.lng(),sw.lat()]);
svg.select('#countries').attr('transform',
'scale('+width/(p1[0]-p2[0])+','+height/(p2[1]-p1[1])+')'+
'translate('+(-p2[0])+','+(-p1[1])+') ');
svg.selectAll('path').attr('d', path);
}
</script>
</body>
</html>