block by patricksurry 6511981

Illustrate embedding geojson/topojson geometry into a google maps overlay using d3.geo.projection.

Full Screen

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.

index.html

<!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>