// move elements to front // http://tributary.io/tributary/3922684 d3.selection.prototype.moveToFront = function() { return this.each(function(){ this.parentNode.appendChild(this); }); }; var width = 960, height = 500, active = d3.select(null); //zoom // set projection and map position // tweek these settings for a nice display var projection = d3.geo.albers() // popular alternative is geo.mercator() .center([0, 40.0]) //[30, 45.0] .scale(900) // 275 .rotate([97.8717, 0]) .translate([width / 2, height / 2]) .precision(.1); var path = d3.geo.path() .projection(projection); var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height) .attr("class", "chart"); var graticule = d3.geo.graticule(); var dataset; var destination = [-75.1667 , 39.9500]; // Philly lat lon var map = svg.append("g") .attr("class", "map"); var vizGroup = svg.append("g").attr("class", "viz"), countryGroup = map.append("g"), stateGroup = map.append("g") ; // plot world, states, and people data queue() .defer(d3.json, "world-50m.json") .defer(d3.json, "us.json") .defer(d3.csv, "people.csv") .await(visualize); function visualize(error, world, us, people){ if (error) return console.log(error); dataset = people; // draw backgound to for reset function map.append("rect") .attr("class", "background") .attr("width", width) .attr("height", height) .on("click", reset); // draw graticules map.append("path") .datum(graticule) .attr("class", "graticule") .attr("d", path); // draw world countryGroup .selectAll("path") .data(topojson.feature(world, world.objects.countries).features) .enter().insert("path", ".graticule") .attr("class", "boundary") .attr("d", path); // draw states stateGroup .selectAll("path") .data(topojson.feature(us, us.objects.states).features) .enter().append("path") // .attr("d", path) .attr("class", "state-boundary") .attr("d", path); // create a group for each person record var groups = vizGroup.selectAll("g") .data(dataset) .enter() .append("g") .attr("class", function (d) { return d.name; }) .on("mouseover", function() { d3.select(this).attr("class", "highlight"); // move to front var sel = d3.select(this); sel.moveToFront(); }) .on("mouseout", function (d) { d3.select(this).attr("class", function () { return d.name;}); }) ; // draw arcs from each record origin to destination groups.append("path") .attr("class", "arc") .attr("d", function (d) { var coordDepart = [ d.longitude, d.latitude ]; var coordArrive = destination; return path({ type: "LineString", coordinates: [coordDepart,coordArrive] }); }) .call(d3.helper.tooltip(function(d, i){ return ""+ "
" + d.name + "
"+ "
" + d.org + "
"+ "
" + d.place + "
" } )); // draw circles for each record origin groups.append("circle") .attr("cx", function(d) { return projection([d.longitude, d.latitude])[0]; }) .attr("cy", function(d) { return projection([d.longitude, d.latitude])[1]; }) .attr("r", function(d) { return 4 + (d.count * 2); }) .attr("class", "origin") .call(d3.helper.tooltip(function(d, i){ return ""+ "
" + d.name + "
"+ "
" + d.org + "
"+ "
" + d.place + "
" } )) .on("click", clicked); // zoom function clicked(d) { if (active.node() === this) return reset(); active.classed("active", false); active = d3.select(this).classed("active", true); var cx = projection([d.longitude, d.latitude])[0], cy = projection([d.longitude, d.latitude])[1], scale = 3.75, translate = [width / 2 - scale * cx, height / 2 - scale * cy]; // TODO how to link this to main.css? d3.selectAll(".chart circle").transition() .duration(1000) //750 .style("stroke-width", 1 / scale *1.5 + "px") // *1.5 is just a fudge .attr("r", 6 / scale *1.5 ) .attr("transform", "translate(" + translate + ")scale(" + scale + ")"); d3.selectAll(".chart path").transition() .duration(1000) //750 .style("stroke-width", 2 / scale *1.5 + "px") // *1.5 is just a fudge .attr("transform", "translate(" + translate + ")scale(" + scale + ")"); } // TODO how to link this to main.css? function reset() { active.classed("active", false); active = d3.select(null); d3.selectAll(".chart path, circle").transition() .duration(1000) //750 .style("stroke-width", "1.5px") // this is a fudge .attr("r", 6 ) .attr("transform", ""); } // backgound for sidebar groups.append("rect") .attr("class", "side-menu") .attr("y", function(d, i) { return (0 + (20 * i)); }) .attr("x", "0") .attr("width", "150") .attr("height", "20") .call(d3.helper.tooltip(function(d, i){ return "" +"
" + d.name + "
" +"
" + d.org + "
" } )) .on("click", clicked); ; // add sidebar list of names for each person record groups.append("text") .attr("class", "speaker-list") .attr("y", function(d, i) { return (15 + (20 * i)); }) .attr("x", "10") .text(function(d) {return d.name;}) .call(d3.helper.tooltip(function(d, i){ return ""+ "
" + d.name + "
"+ "
" + d.org + "
"+ "
" + d.place + "
" } )) .on("click", clicked); ; }; d3.select(self.frameElement).style("height", height + "px"); // universal zoom and pan listener, disabled // http://bl.ocks.org/d3noob/5189284 // var zoom = d3.behavior.zoom() // .on("zoom",function() { // d3.selectAll(".chart path, circle").attr("transform","translate("+ // d3.event.translate.join(",")+")scale("+d3.event.scale+")"); // d3.selectAll(".chart path, circle").selectAll("path") // .attr("d", path.projection(projection)); // }); // svg.call(zoom)