Skip to content

Instantly share code, notes, and snippets.

@pbogden
Last active July 5, 2017 16:07
Show Gist options
  • Save pbogden/6056674 to your computer and use it in GitHub Desktop.
Save pbogden/6056674 to your computer and use it in GitHub Desktop.
Google & D3

D3 & Google Maps

This version of mbostock's block does a couple things differently.

  1. Sets the viewport with data using google.maps.Map().fitBounds()
  2. Puts all data on a single SVG (rather than one SVG per data point)
  3. Puts the SVG on the overlayMouseTarget pane; this pane receives mouse events
  4. Each station has a gray circle that can be dragged to a new location
<!DOCTYPE html>
<meta charset="utf-8">
<title>google-d3</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<style>
html, body, #map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.marker {
fill: brown;
stroke: black;
stroke-width: 1.5px;
pointer-events: none;
}
</style>
<div id="map"></div>
<script src="//maps.google.com/maps/api/js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var svg, rect, circles, markers, labels;
// Create the Google Map…
var map = new google.maps.Map(d3.select("#map").node(), {
zoom: 8,
center: new google.maps.LatLng(37.76487, -122.41948), // San Francisco
draggable: false, // Disable map panning
mapTypeId: google.maps.MapTypeId.TERRAIN
});
// Drag behavior
var drag = d3.behavior.drag()
.on("drag", dragmove);
// Load the station data. When the data comes back, create an overlay.
d3.json("stations.json", function(error, data) {
if (error) throw error;
var projection;
var bounds = getBB( data ); // returns a google.maps.LatLngBounds object
var overlay = new google.maps.OverlayView();
// Set the viewport to contain the bounding box for the data
map.fitBounds( bounds );
// Bind our overlay to the map…
overlay.setMap(map);
// Add the SVG container to the overlayMouseTarget pane; this pane receives mouse events
overlay.onAdd = function() {
// The panes aren't unitialized until the API calls .onAdd()
svg = d3.select(this.getPanes().overlayMouseTarget).append("div")
.append("svg")
.style("position", "absolute")
rect = svg.append("rect")
.style("background", "gray")
.style("opacity", 0.1)
g = svg.selectAll("circle")
.data(d3.entries(data))
.enter()
.append("g");
circles = g.append("circle")
.style({"fill": "black", "opacity": 0.5})
.attr("r", 8.5)
.style("cursor", "pointer")
.call(drag);
markers = g.append("circle")
.attr("class", "marker")
.attr("r", 4.5)
// Add a label.
labels = g.append("text")
.attr("dx", "1em")
.attr("dy", ".31em")
.text(function(d) { return d.key; });
};
overlay.draw = function() {
// The projection isn't initialized until the API calls .draw()
projection = this.getProjection();
var padding = 10;
var border = 50;
var sw = projection.fromLatLngToDivPixel(bounds.getSouthWest());
var ne = projection.fromLatLngToDivPixel(bounds.getNorthEast());
var width = ne.x - sw.x + 2 * border;
var height = sw.y - ne.y + 2 * border;
// One SVG for all the data
svg.style("left", (sw.x - border) + 'px')
.style("top", (ne.y - border) + 'px')
.style("width", width + "px")
.style("height", height + "px")
rect.attr("width", width + "px")
.attr("height", height + "px")
circles
.attr("cx", x)
.attr("cy", y);
markers
.attr("cx", x)
.attr("cy", y)
labels
.attr("x", x)
.attr("y", y)
function x(d) {
var p = new google.maps.LatLng(d.value[1], d.value[0]);
p = projection.fromLatLngToDivPixel(p);
return p.x - sw.x + border;
}
function y(d) {
var p = new google.maps.LatLng(d.value[1], d.value[0]);
p = projection.fromLatLngToDivPixel(p);
return p.y - ne.y + border;
}
}; // end of overlay.draw
});
// Get bounding box for the data
function getBB(data) {
var north = d3.max( d3.values(data).map(function(d) { return d[1]; }) );
var south = d3.min( d3.values(data).map(function(d) { return d[1]; }) );
var east = d3.max( d3.values(data).map(function(d) { return d[0]; }) );
var west = d3.min( d3.values(data).map(function(d) { return d[0]; }) );
// return google.maps.LatLngBounds()
var sw = new google.maps.LatLng({ lat: south, lng: west });
var ne = new google.maps.LatLng({ lat: north, lng: east });
return new google.maps.LatLngBounds( sw, ne);
}
function dragmove(d) {
d3.select(this)
.attr("cx", d.x = d3.event.x)
.attr("cy", d.y = d3.event.y);
}
</script>
{"KMAE":[-120.12,36.98],"KSJC":[-121.92,37.37],"KMCE":[-120.50,37.28],"KMER":[-120.57,37.37],"KAPC":[-122.28,38.20],"KSUU":[-121.95,38.27],"KSQL":[-122.25,37.52],"KSNS":[-121.60,36.67],"KMOD":[-120.95,37.62],"KOAK":[-122.23,37.72],"KSCK":[-121.23,37.90],"KCCR":[-122.05,38.00],"KMRY":[-121.85,36.58],"KPAO":[-122.12,37.47],"KSAC":[-121.50,38.50],"KHWD":[-122.12,37.67],"KSTS":[-122.82,38.50],"KSMF":[-121.60,38.70],"KNUQ":[-122.05,37.43],"KRHV":[-121.82,37.33],"KWVI":[-121.78,36.93],"KMHR":[-121.30,38.55],"KVCB":[-121.95,38.38],"KSFO":[-122.37,37.62],"KLVK":[-121.82,37.70]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment