Skip to content

Instantly share code, notes, and snippets.

@patricksurry
Last active November 22, 2019 18:46
Show Gist options
  • Save patricksurry/6511981 to your computer and use it in GitHub Desktop.
Save patricksurry/6511981 to your computer and use it in GitHub Desktop.
Illustrate embedding geojson/topojson geometry into a google maps overlay using d3.geo.projection.

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="http://maps.google.com/maps/api/js?sensor=true"></script>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://d3js.org/queue.v1.min.js"></script>
<script src="http://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>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@succinct
Copy link

I'm also noticing an issue with panning. The overlay layer moves at a different scale than the underlaying map, so it "floats" on top of it. Any idea what might fix this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment