Skip to content

Instantly share code, notes, and snippets.

@iblind
Created November 5, 2015 18:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save iblind/6c75592f5053a86c5480 to your computer and use it in GitHub Desktop.
Save iblind/6c75592f5053a86c5480 to your computer and use it in GitHub Desktop.
city number_of_posts population_ranking state estimate_2014 actual_2010 post_rank lat lon posts_per_10k ratio_rank city_shrt rPop
Dallas 1581 9 Texas 1,281,047 1,197,816 3 32.769582 -96.791764 12.34146757 1 DAL 0.2191423
Phoenix 1356 6 Arizona 1,537,058 1,445,632 4 33.462898 -112.076986 8.822048355 2 PHX 0.2400428
San Diego 1168 8 California 1,381,069 1,307,402 5 32.745467 -117.154215 8.457216837 3 SDG 0.2275366
Chicago 1938 3 Illinois 2,722,389 2,695,598 2 41.864933 -87.625039 7.118747541 4 CHI 0.3194616
Philadelphia 940 5 Pennsylvania 1,560,297 1,526,006 7 39.962926 -75.159232 6.024494055 5 PHIL 0.2418507
Houston 1021 4 Texas 2,239,558 2,100,263 6 29.748644 -95.362839 4.55893529 6 HOU 0.2897509
San Antonio 624 7 Texas 1,436,697 1,327,407 9 29.409079 -98.489685 4.343295768 7 SAN 0.2320739
New York 2669 1 New York 8,491,079 8,175,133 1 40.71455 -74.007124 3.143298985 8 NYC 0.5641896
Los Angeles 718 2 California 3,928,864 3,792,621 8 34.037002 -118.234578 1.827500265 9 LA 0.3837754
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> All the missed connections, where do they all come from?</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<style>
body {
font-family:Arial, Helvetica, sans-serif;
}
.axisGeo path,
.axisGeo line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axisGeo text {
font-family: sans-serif;
font-size: 11px;
}
#allTooltips {
position: relative;
}
#tooltip_3, #tooltip_1 {
position: absolute;
top:-15px;
right:45%;
width: 270px;
height: 80px;
padding: 5px;
background-color: white;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
-moz-box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
pointer-events: none;
z-index: 10;
opacity: 0.9;
}
#tooltip_3.hidden, #tooltip_1.hidden {
display: none;
}
#tooltip_3, #tooltip_1 {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
line-height: 14px;
}
.cityshort .cityshort2 {
font-size: 13px,
fill: MidnightBlue,
font-family: Arial
}
#tooltip_2 {
position: absolute;
top:-15px;
right:45%;
width: 270px;
height: 80px;
padding:8px;
background-color: white;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
-webkit-box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
-moz-box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
pointer-events: none;
z-index: 10;
opacity: 1;
}
#tooltip_2.hidden {
display: none;
}
#tooltip_2 {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
line-height: 14px;
}
.legendGeo circle {
fill: none;
stroke: #ccc;
}
span#city, span#city2 {
font-size: 24px;
}
</style>
</head>
<body>
<!-- Adding divs for buttons above graph and mouseover tooltips -->
<div id="buttonsGeo">
<div id="popGeo">Population</div>
<div id="postsGeo">Posts</div>
<div id="plotGeo">Posts/population</div>
</div>
<div id="allTooltips">
<div id ="tooltip_1" class="hidden">
<p><center><strong><span id="city"></span></strong></center></p>
<p><center>Population: <span id="popvalue"></span><center></p>
</div>
<div id ="tooltip_2" class="hidden">
<p><center><strong><span id="city2"></span></strong></center></p>
<p><center><span id="value2"></span> missed connections for 10,000 residents</p></center>
</div>
<div id ="tooltip_3" class="hidden">
<p><center><strong><span id="city"></span></strong></center></p>
<p><center>Missed connections: <span id="valueGeo"></span></center></p>
</div>
</div>
<br>
<!-- svg -->
<div id="mapGeo">
</div>
<script type="text/javascript">
//Adding global variables
var paddingGeo = {top: 10, left: 10, bottom: 10, right: 10},
wGeo = parseInt(d3.select('#mapGeo').style('width')),
wGeo = wGeo - paddingGeo.left - paddingGeo.right,
mapRatio = .5,
hGeo = wGeo * mapRatio,
projection = d3.geo.albersUsa().scale(wGeo).translate([wGeo/2, hGeo/2]);
path = d3.geo.path().projection(projection);
//Loading the map, + its relevant JSON
d3.json("us-states.json", function(json){
function resize(){
wGeo = parseInt(d3.select('#mapGeo').style('width'));
wGeo = wGeo - paddingGeo.left - paddingGeo.right;
hGeo = wGeo * mapRatio;
projection.translate([wGeo/2, hGeo/2]).scale(wGeo);
svgGeo.selectAll("path")
.attr("d", path);
}
var svgGeo = d3.select('#mapGeo').append('svg')
.style('height', hGeo+ 'px')
.style('width', wGeo +'px')
d3.select(window).on('resize', resize);
svgGeo.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr({
"d": path,
"fill": "#B0B0B0",
"stroke": "white",
"stroke-width": 0.3,
"opacity": 1,
class: "map"
});
d3.csv("cities_pop.csv", function(data){
//Setting scale for circle radius
var rScaleGeo = d3.scale.linear()
.domain([0, d3.max(data, function(d){
return Math.sqrt(d.estimate_2014.replace(/\,/g,''));})])
.range([1,15]);
//Adding first set of circles, to be used as bottom row of scatter plot
svgGeo.selectAll("circle.secondary")
.data(data)
.enter()
.append("circle")
.attr({
class:"secondary",
"cx": function(d){return projection([d.lon, d.lat])[0];},
"cy": function(d){return projection([d.lon, d.lat])[1];},
"r": 1
})
.style({
"fill": "#43a2ca",
"opacity": 0,
"stroke-width":0.7,
"stroke":"white",
})
//Adding circles to the map
svgGeo.selectAll("circle.regular")
.data(data)
.enter()
.append("circle")
.attr({
class:"regular",
"cx": function(d){return projection([d.lon, d.lat])[0];},
"cy": function(d){return projection([d.lon, d.lat])[1];},
"r": function(d){return rScaleGeo(Math.sqrt(d.estimate_2014.replace(/\,/g,'')));}
})
.style({
"fill": "#43a2ca",
"opacity": 0,
"stroke-width":0.7,
"stroke":"white",
})
.transition()
.duration(500)
.ease("circle")
.style("opacity", 1);
//Adding tooltip mousover + mouseout functionality
svgGeo.selectAll("circle").on("mouseover", function(d) {
//Update the tooltip value
d3.select("#tooltip_1")
.select("#popvalue")
.text(d.estimate_2014);
d3.select("#tooltip_1")
.select("#city")
.text(d.city);
//Show the tooltip
d3.select("#tooltip_1")
.classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip_1").classed("hidden", true)
});
//ADDING "POPULATION" BUTTON FUNCTIONALITY
d3.select("#popGeo")
.on("click", function(){
//Removing x + y labels from scatterplot; removing legends(circle+text)
d3.selectAll("text.xlabelGeo").remove();
d3.selectAll("circle.legendGeo").remove();
d3.selectAll("text.legendGeo").remove();
d3.selectAll("g").remove();
//Reconfiguring rScale
var rScaleGeo = d3.scale.linear()
.domain([0, d3.max(data, function(d){
return parseFloat(d.estimate_2014);
})])
.range([5,15]);
//Restoring map's opacity to it appears in foreground
d3.selectAll("path.map")
.transition()
.delay(10)
.duration(700)
.attr({
"stroke-width": 0.5,
"opacity": 1
});
//Removing the top and bottom text labels added during scatterplot step
d3.selectAll("text.cityshort")
.remove();
d3.selectAll("text.cityshort2")
.remove();
//Moving bottom row of circles back to initial positions, below regular circles
d3.selectAll("circle.secondary")
.transition()
.attr({
"cx": function(d){return projection([d.lon, d.lat])[0];},
"cy": function(d){return projection([d.lon, d.lat])[1];},
"r": 1
})
.style({
"fill": "#43a2ca",
"opacity": 0,
"stroke-width":0.7,
"stroke":"white",
})
//Moving top row of circles back to their rightful place, as is their birthright
d3.selectAll("circle.regular")
.transition()
.duration(500)
.attr({
class:"regular",
"cx": function(d){return projection([d.lon, d.lat])[0];},
"cy": function(d){return projection([d.lon, d.lat])[1];},
"r": function(d){return rScaleGeo(parseFloat(d.estimate_2014));}
})
.style({
"fill": "#43a2ca",
"opacity": 0,
"stroke-width":0.7,
"stroke":"white",
"opacity": 1
});
//Re-adding tooltip mousover + mouseout functionality
svgGeo.selectAll("circle").on("mouseover", function(d) {
//Update the tooltip position and value
d3.select("#tooltip_1")
.select("#popvalue")
.text(d.estimate_2014);
d3.select("#tooltip_1")
.select("#city")
.text(d.city);
//Show the tooltip
d3.select("#tooltip_1")
.classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip_1").classed("hidden", true)
});
});//End of "posts" button
//ADDING "POSTS" BUTTON FUNCTIONALITY
d3.select("#postsGeo")
.on("click", function(){
//Removing x + y labels from scatterplot; removing legends(circle+text)
d3.selectAll("text.xlabelGeo").remove();
d3.selectAll("circle.legendGeo").remove();
d3.selectAll("text.legendGeo").remove();
d3.selectAll("g").remove();
//Reconfiguring rScale
var rScaleGeo = d3.scale.linear()
.domain([0, d3.max(data, function(d){
return parseFloat(d.number_of_posts);
})])
.range([5,15]);
//Restoring map's opacity to it appears in foreground
d3.selectAll("path.map")
.transition()
.delay(10)
.duration(700)
.attr({
"stroke-width": 0.5,
"opacity": 1
});
//Removing the top and bottom text labels added during scatterplot step
d3.selectAll("text.cityshort")
.remove();
d3.selectAll("text.cityshort2")
.remove();
//Moving bottom row of circles back to initial positions, below regular circles
d3.selectAll("circle.secondary")
.transition()
.attr({
"cx": function(d){return projection([d.lon, d.lat])[0];},
"cy": function(d){return projection([d.lon, d.lat])[1];},
"r": 1
})
.style({
"fill": "#43a2ca",
"opacity": 0,
"stroke-width":0.7,
"stroke":"white",
})
//Moving top row of circles back to their rightful place, as is their birthright
d3.selectAll("circle.regular")
.transition()
.duration(500)
.attr({
class:"regular",
"cx": function(d){return projection([d.lon, d.lat])[0];},
"cy": function(d){return projection([d.lon, d.lat])[1];},
"r": function(d){return rScaleGeo(parseFloat(d.number_of_posts));}
})
.style({
"fill": "#FF1493",
"opacity": 0,
"stroke-width":0.7,
"stroke":"white",
"opacity":1
});
//Re-adding tooltip mousover + mouseout functionality
svgGeo.selectAll("circle").on("mouseover", function(d) {
//Update the tooltip position and value
d3.select("#tooltip_3")
.select("#valueGeo")
.text(d.number_of_posts);
d3.select("#tooltip_3")
.select("#city")
.text(d.city);
//Show the tooltip
d3.select("#tooltip_3")
.classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip_3").classed("hidden", true)
});
});//End of "posts" button
d3.select("#plotGeo").on("click", function(){
//GENERAL PREPPING FOR SCATTERPLOT TRANSITION:
//Fade the map of the U.S.
d3.selectAll("path.map")
.transition()
.delay(10)
.duration(1000)
.attr({
"stroke-width": 0,
"opacity": 0
});
//Remove older elements in case user clicks twice
d3.selectAll("text.xlabelGeo").remove();
d3.selectAll("circle.legendGeo").remove();
d3.selectAll("text.legendGeo").remove();
d3.selectAll("g").remove();
//Adding scales for:
//radius, when scaled for POPULATION of each city
//radius, when scaled for POSTS in each city
//xAxis, to show the order of rankings
//NOTE::: no need for yAxis, since this stays fixed
var xScaleGeo = d3.scale
.linear()
.domain([1, d3.max(data, function(d){ return +d.ratio_rank;})])
.rangeRound([0+paddingGeo, wGeo-paddingGeo]);
var rScalePostsGeo = d3.scale
.linear()
.domain([0, d3.max(data, function(d){ return d.number_of_posts;})])
.rangeRound([2,15]);
var rScalePopGeo = d3.scale
.linear()
.domain([0, d3.max(data, function(d){return parseFloat(d.estimate_2014);})])
.rangeRound([5,20]);
var xAxisGeo = d3.svg.axis()
.scale(xScaleGeo)
.orient("top");
var xAxis2Geo = d3.svg.axis()
.scale(xScaleGeo)
.orient("bottom");
//BEGIN CHANGES TO SVG
//Moving top row of circles from map to ranked row
svgGeo.selectAll("circle.regular")
.transition()
.delay(10)
.duration(500)
.attr({
cx: function(d){ return xScaleGeo(+d.ratio_rank);},
cy: 150,
"r": function(d){ return rScalePostsGeo(d.number_of_posts);}
})
.style({
"fill": "white",
"stroke": "#FF1493",
"stroke-width": 2
});
//Moving bottom row of circles from map to ranked row
svgGeo.selectAll("circle.secondary")
.transition()
.delay(10)
.duration(500)
.attr({
cx: function(d){ return xScaleGeo(+d.ratio_rank);},
cy: 315,
"r": function(d){ return rScalePopGeo(parseFloat(d.estimate_2014));}
})
.style({
"fill": "white",
"stroke": "#43a2ca",
"stroke-width": 2,
"opacity": 1
});
//Adding text legends to the ranked scatterplot
svgGeo.selectAll("text.cityshort")
.data(data)
.enter()
.append("text")
.text(function(d){ return d.city_shrt;})
.attr("opacity", 0)
.transition()
.delay(500)
.attr({
class:"cityshort",
x: function(d,i){return xScaleGeo(+d.ratio_rank)-12;},
y: 100,
"opacity": 1,
"font-size": "13px",
"fill": "MidnightBlue",
"font-family": "arial"
});
svgGeo.selectAll("text.cityshort2")
.data(data)
.enter()
.append("text")
.text(function(d){ return d.city_shrt;})
.attr("opacity", 0)
.transition()
.delay(500)
.attr({
class:"cityshort2",
x: function(d,i){return xScaleGeo(+d.ratio_rank)-12;},
y: 370,
"opacity": 1,
"font-size": "13px",
"fill": "MidnightBlue",
"font-family": "arial"
});
//Add circle size legends
svgGeo.append("circle")
.attr({
class: "legendGeo",
cx: 45,
cy: (hGeo/2)-210,
"r": 15,
})
.style({
"fill": "White",
"stroke": "#FF1493",
"stroke-width": 2,
"opacity": 0
})
.transition()
.duration(500)
.style("opacity", 1);
svgGeo.append("circle")
.attr({
class: "legendGeo",
cx: 45,
cy: (hGeo/2)+200,
"r": 15,
})
.style({
fill: "White",
stroke: "#43a2ca",
"stroke-width": 2,
"opacity": 0
})
.transition()
.duration(500)
.style("opacity", 1);
//Legend circle text
svgGeo.append("text")
.attr({
class: "legendGeo",
x: 33,
y: 17,
"fill": "LightSlateGray",
"font-size": 10
})
.style("opacity", 0)
.text("Posts")
.transition()
.delay(500)
.style("opacity", 1);
svgGeo.append("text")
.attr({
class: "legendGeo",
x: 22,
y: 479,
"fill": "LightSlateGray",
"font-size": 10
})
.text("Population")
.style("opacity", 0)
.transition()
.delay(500)
.style("opacity", 1);
//Adding text labels to x Axes
svgGeo.append("text")
.transition()
.delay(500)
.attr("class", "xlabelGeo")
.attr("text-anchor", "end")
.attr("x", wGeo-115)
.attr("y", hGeo/2)
.text("Rank by number of missed connections for every 10,000 inhabitants");
//Adding x Axes
svgGeo.append("g")
.attr("class", "axisGeo")
.attr("transform", "translate(0,"+ 220+")")
.call(xAxisGeo);
svgGeo.append("g")
.attr("class", "axisGeo")
.attr("transform", "translate(0,"+ 270+")")
.call(xAxis2Geo);
//Adding mouseover
svgGeo.selectAll("circle").on("mouseover", function(d) {
//Update the tooltip position and value
d3.select("#tooltip_2")
.select("#value2")
.text(d.posts_per_10k.toString().substr(0,4));
d3.select("#tooltip_2")
.select("#city2")
.text(d.city);
//Show the tooltip
d3.select("#tooltip_2").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip_2").classed("hidden", true)
});
});//End of plotting
}); //End of CSV load
}); //End of JSON load
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment