Skip to content

Instantly share code, notes, and snippets.

@mbostock
Last active November 14, 2023 03:46
  • Star 58 You must be signed in to star a gist
  • Fork 37 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mbostock/899711 to your computer and use it in GitHub Desktop.
Google Maps + D3
license: gpl-3.0
<!DOCTYPE html>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<style>
html, body, #map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.stations, .stations svg {
position: absolute;
}
.stations svg {
width: 60px;
height: 20px;
padding-right: 100px;
font: 10px sans-serif;
}
.stations circle {
fill: brown;
stroke: black;
stroke-width: 1.5px;
}
</style>
<div id="map"></div>
<script src="//maps.google.com/maps/api/js?sensor=true"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// 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),
mapTypeId: google.maps.MapTypeId.TERRAIN
});
// Load the station data. When the data comes back, create an overlay.
d3.json("stations.json", function(error, data) {
if (error) throw error;
var overlay = new google.maps.OverlayView();
// Add the container when the overlay is added to the map.
overlay.onAdd = function() {
var layer = d3.select(this.getPanes().overlayLayer).append("div")
.attr("class", "stations");
// Draw each marker as a separate SVG element.
// We could use a single SVG, but what size would it have?
overlay.draw = function() {
var projection = this.getProjection(),
padding = 10;
var marker = layer.selectAll("svg")
.data(d3.entries(data))
.each(transform) // update existing markers
.enter().append("svg")
.each(transform)
.attr("class", "marker");
// Add a circle.
marker.append("circle")
.attr("r", 4.5)
.attr("cx", padding)
.attr("cy", padding);
// Add a label.
marker.append("text")
.attr("x", padding + 7)
.attr("y", padding)
.attr("dy", ".31em")
.text(function(d) { return d.key; });
function transform(d) {
d = new google.maps.LatLng(d.value[1], d.value[0]);
d = projection.fromLatLngToDivPixel(d);
return d3.select(this)
.style("left", (d.x - padding) + "px")
.style("top", (d.y - padding) + "px");
}
};
};
// Bind our overlay to the map…
overlay.setMap(map);
});
</script>
{"KMAE":[-120.12,36.98,"MADERA MUNICIPAL AIRPORT",[26,1,2,5,6,3,2,1,2,7,29,12,3]],"KSJC":[-121.92,37.37,"SAN JOSE INTERNATIONAL AIRPORT",[28,1,1,1,6,10,5,3,2,4,14,21,7]],"KMCE":[-120.50,37.28,"MERCED MUNICIPAL AIRPORT",[29,1,1,3,7,5,2,1,3,6,12,26,5]],"KMER":[-120.57,37.37,"Merced / Castle Air Force Base",[34,1,1,1,4,5,2,1,1,4,17,22,7]],"KAPC":[-122.28,38.20,"NAPA COUNTY AIRPORT",[23,2,1,6,3,3,8,18,11,13,4,3,5]],"KSUU":[-121.95,38.27,"Fairfield / Travis Air Force Base",[13,7,4,3,3,6,4,13,33,4,1,2,7]],"KSQL":[-122.25,37.52,"San Carlos Airport",[18,3,2,2,3,4,3,2,5,17,16,12,12]],"KSNS":[-121.60,36.67,"SALINAS MUNICIPAL AIRPORT",[21,1,1,6,12,3,1,2,9,21,17,5,1]],"KMOD":[-120.95,37.62,"MODESTO CITY CO SHAM FLD",[27,1,1,2,10,5,1,1,1,3,17,24,8]],"KOAK":[-122.23,37.72,"METRO OAKLAND INTERNATIONAL AIRPORT ",[16,3,3,2,4,6,3,4,9,23,20,6,2]],"KSCK":[-121.23,37.90,"STOCKTON METROPOLITAN AIRPORT ",[21,2,2,3,6,8,2,1,4,15,19,12,4]],"KCCR":[-122.05,38.00,"CONCORD BUCHANAN FIELD",[24,3,2,1,1,5,17,12,9,9,7,6,4]],"KMRY":[-121.85,36.58,"MONTEREY PENINSULA AIRPORT",[26,1,2,9,5,3,4,9,13,14,9,4,1]],"KPAO":[-122.12,37.47,"Palo Alto Airport",[31,3,1,1,2,5,1,1,1,4,10,25,14]],"KSAC":[-121.50,38.50,"SACRAMENTO EXECUTIVE AIRPORT ",[32,1,0,1,3,11,12,16,5,2,4,9,3]],"KHWD":[-122.12,37.67,"HAYWARD AIR TERMINAL",[20,2,7,2,2,6,3,3,6,23,18,6,2]],"KSTS":[-122.82,38.50,"SANTA ROSA SONOMA COUNTY",[46,1,0,1,5,13,10,4,3,3,4,6,3]],"KSMF":[-121.60,38.70,"SACRAMENTO INTERNATIONAL AIRPORT",[19,2,1,2,4,21,18,8,3,2,5,12,4]],"KNUQ":[-122.05,37.43,"MOFFETT FIELD",[35,3,1,1,4,7,2,1,2,5,6,17,15]],"KRHV":[-121.82,37.33,"San Jose / Reid / Hillv",[35,0,0,1,4,4,2,1,1,10,28,11,1]],"KWVI":[-121.78,36.93,"WATSONVILLE MUNICIPAL AIRPORT ",[44,1,2,3,4,5,7,9,8,4,6,5,2]],"KMHR":[-121.30,38.55,"Sacramento, Sacramento Mather Airport",[21,1,1,2,8,15,12,12,7,4,5,7,3]],"KVCB":[-121.95,38.38,"VACAVILLE NUT TREE AIRPORT",[36,2,1,1,2,6,10,18,10,2,2,5,6]],"KSFO":[-122.37,37.62,"SAN FRANCISCO INTERNATIONAL AIRPORT ",[13,3,3,2,3,4,4,4,7,31,20,2,3]],"KLVK":[-121.82,37.70,"LIVERMORE MUNICIPAL AIRPORT ",[32,2,7,3,1,1,2,7,9,17,16,2,1]]}
@constantx
Copy link

ah, nm, it turns out some css style is needed. In my case, this was needed:

.stations, .stations svg {
  position: absolute;
}

@igwtsoftware
Copy link

any examples on how to add d3 circles in real time as data is received?

@tgsmith61591
Copy link

I, too, am looking for a solution to mouseover/onclick events for the svg circles. Anyone found anything?

@tgsmith61591
Copy link

I ended up answering my own question. You have to make a couple changes. First make the overlayLayer an overlayMouseTarget like so:

var layer = d3.select(this.getPanes().overlayMouseTarget).append("div") .attr("class", "stations");

I added the following attr to the circle:

marker.append("svg:circle").attr("r", 4.5).attr("cx", padding).attr("cy", padding).on("click",toggleExpand);

...and then had to define the toggleExpand() method:

function toggleExpand() { if(d3.select(this).attr("r") == 4.5) { d3.select(this).transition().duration(100).attr("r",7) } else { d3.select(this).transition().duration(100).attr("r",4.5) } }

(please forgive the formatting, I can't make the function look good)

@adamspe
Copy link

adamspe commented May 29, 2015

Thanks for this example it got me heading in the right direction.

I copied it (didn't fork) and made some changes for my own purposes so that I could use a single SVG instead of one per map marker and like several others mentioned so I could have my markers get mouse events (using overlayMouseTarget instead of overlayLayer.

I did a small writeup of my changes here and the end result is here.

@xvilar
Copy link

xvilar commented Sep 4, 2015

I've already loaded data from Elasticsearch, doing a query to ES, into an object called "resp"... I've this values:

"hits": {
"hits": [
"_source": {"location": "43.54526,-5.661926"},
"_source": {"location": "40.41669,-3.700346"}
]}

so values from "resp.hits.hits._source.location" is what i want to draw in the map

I've tried to adapt the code with

var marker = layer.selectAll("svg")
.data(resp.hits.hits)

and:

d = new google.maps.LatLng(d.location)
or
d = new google.maps.LatLng(d.location[1],d.location[0])

It doens't works ¿? Thanks!

@powersparks
Copy link

In some cases, the map is part of a document, that contains some css like the following:
*, *::before, *::after {
box-sizing: border-box;
}

if you add the above css to the example, the overlay div is still placed but not rendered.
Highlighting each html svg element shows it's in the right location, but the content is not displayed.

any idea why?

@ashikvs
Copy link

ashikvs commented Oct 25, 2017

hi mbostock, i took a copy of your code and tweaked it a little bit to get live lat and long from an api, by this i could get live tracking of my car!
but now i want just only the live locations to be plotted on the map , and aviod the trailing points, but i dont know how to remove the trailing points.. can someone tell me how?
tracking

@ashikvs
Copy link

ashikvs commented Oct 26, 2017

i got the solution to the described problem, we have to remove the existing svg layer us9ing the folowing command
d3.select("svg").remove();
Thanks anyways :)

@lipearu
Copy link

lipearu commented Nov 2, 2017

if i add the <div id="map"></div> inside another div, the map not show

@AnushaGanta
Copy link

Hi,
Can someone help me with drawing a line between two points? My main idea is to not just plot nodes but to plot a graph over a map. Since, the above code shows how to plot the nodes, can some one help me with plotting lines between the plotted nodes?
Thank you in advance.

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