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.
Last active
November 22, 2019 18:46
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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?