xxxxxxxxxx
<html lang='en-GB'>
<head></head>
<style>
h3 {
font-family: sans-serif;
font-size: 22px;
font-weight: 700;
line-height: 26px;
margin: -5px 0 -5px 0;
padding: 10px 0 0 0;
}
.g-sentence-div {
margin: 10px 0 0 0;
}
.g-sentence {
font-family: sans-serif;
font-size: 16px;
margin: 0;
}
.g-country {
font-family: sans-serif;
font-size: 16px;
margin: 0;
font-weight: 700;
}
p {
font-family: sans-serif;
font-size: 14px;
}
/*template styles*/
.heds {
margin-bottom: 20px;
}
.gia-chart-wrapper {
max-width: 960px;
margin: 0 auto;
}
.heds {
float: left;
width: 62%;
}
.gia-chart {
width: 100%;
clear:both;
}
.g-chart-container {
width: 33.3%;
float: left;
margin: 0;
}
.g-chart-container:last-of-type {
margin-bottom: 15px;
}
.key {
float: right;
}
.keyblock {
display: inline-block;
margin: 13px 2px 0 0;
position: relative;
height: 50px;
width: 50px;
}
.circle10000 {
border-radius: 50%;
border: 1px solid #b82266;
background-color: #ebb2c3;
width: 17px;
height: 17px;
opacity: 0.5;
position: absolute;
bottom: 0px;
}
.circle25000 {
border-radius: 50%;
border: 1px solid #b82266;
background-color: #ebb2c3;
width: 25px;
height: 25px;
opacity: 0.5;
position: absolute;
bottom: 0px;
}
.circle50000 {
border-radius: 50%;
border: 1px solid #b82266;
background-color: #ebb2c3;
width: 33px;
height: 33px;
opacity: 0.5;
position: absolute;
bottom: 0px;
}
.circle100000 {
border-radius: 50%;
border: 1px solid #b82266;
background-color: #ebb2c3;
width: 50px;
height: 50px;
opacity: 0.5;
position: absolute;
bottom: 0px;
}
circle.highlighted {
fill: '#ebb2c3';
opacity: 0.7;
stroke: #b82266;
stroke-width: 2;
}
.bil10, .bil25, .bil50, .bil100 {
margin: 0 0 0 0;
font-size: 12px;
text-align: center;
position: absolute;
bottom: -20px;
color: #bdbdbd;
}
.bil10 {
margin-left: -13px;
}
.bil25 {
margin-left: -8px;
}
.bil50 {
margin-left: -5px;
}
/*map styles */
.states {
fill: #e2e2e2;
stroke: #ffffff;
stroke-linejoin: round;
}
.China {
fill: #ebb2c3;
opacity: 0.4;
stroke: #b82266;
stroke-width: 1px;
}
.Germany {
fill: #ebb2c3;
opacity: 0.4;
stroke: #b82266;
stroke-width: 1px;
}
.Canada {
fill: #ebb2c3;
opacity: 0.4;
stroke: #b82266;
stroke-width: 1px;
}
.default {
fill: #ebb2c3;
opacity: 0.4;
stroke: #b82266;
stroke-width: 1px;
}
.label {
font-family: 'Guardian Text Sans Web',Arial,sans-serif;
font-size: 14px;
fill: #333333;
text-anchor: start;
}
.visible {
transition: opacity 0.3s;
opacity: 1;
}
.tooltip {
position: fixed;
text-align: left;
padding: 2px;
font-family: sans-serif;
font-size: 13px;
line-height: 16px;
pointer-events: none;
color: #333333;
opacity: 0;
background-color: #ffffff;
border: 1px solid #dcdcdc;
}
.visible {
transition: opacity 0.3s;
opacity: 1;
}
.bolded {
font-weight: 700;
}
.circleLabels {
opacity: 0;
}
@media(max-width: 900px) {
.g-sentence-div {
margin-right: 80px;
}
}
@media(max-width: 740px) {
.g-chart-container {
width: 50%;
}
.g-sentence-div {
margin-right: 0px;
}
.heds {
width: 55%;
}
}
@media(max-width: 520px) {
.heds {
width: 100%;
margin-bottom: -10px;
}
.key {
float: left;
clear:both;
margin: 0 0 20px 12px;
}
}
@media(max-width: 605px) {
.g-sentence-div {
margin-right: 100px;
}
}
@media(max-width: 500px) {
.g-sentence-div {
margin-right: 40px;
}
}
@media(max-width: 450px) {
.g-chart-container {
width: 100%;
}
}
</style>
<body>
<main>
<div class='gia-chart-wrapper'>
<div class='gia-chart-top'>
<div class="heds">
<h3>The United States' biggest import partners</h3>
<p class="intro">In 2016, China, Mexico, Canada, Japan, Germany and South Korea accounted for over 60 percent of the US' import value in billions.</p>
</div>
<div class="key">
<div class='keyblock'>
<div class="circle10000"></div>
<p class="bil10">$10 bil.</p>
</div>
<div class='keyblock'>
<div class="circle25000"></div>
<p class="bil25">$25 bil.</p>
</div>
<div class='keyblock'>
<div class="circle50000"></div>
<p class="bil50">$50 bil.</p>
</div>
<div class='keyblock'>
<div class="circle100000"></div>
<p class="bil100">$100 bil.</p>
</div>
</div>
</div>
<div class='gia-chart'></div>
</div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script>
//Country list
var allCountries = ["China", "Mexico", "Canada", "Japan", "Germany", "South Korea"];
//Load the files
queue()
.defer(d3.json, "usmap.json")
.defer(d3.csv, "states.csv")
.await(ready);
//Comma formater
var format = d3.format("0,000");
//Make all charts and call resize
function ready(error, us, data) {
if (error) throw error;
//Map the countries to the data
allCountries = d3.set(data.map(function(d) { return d.country; })).values();
//Make a map for each country
var charts = allCountries.map(makeChart);
d3.select(window).on("resize", function() {
charts.forEach(function(chart) {
chart.resize();
});
});
//Create data lookup by importer
var dataByImporter = {};
allCountries.forEach(function (country) {
dataByImporter[country] = {};
});
data.forEach(function (record) {
dataByImporter[record.country][record.FIPS] = record;
});
//Make each individual chart
function makeChart(country) {
//Filter data for each country
var chartData = data.filter(function(d) {
return d.country === country;
});
//Pair data with state id
var dataByFIPS = {};
chartData.forEach(function(d) { dataByFIPS[d.FIPS] = d.value; });
//Pair state name with state id
var stateByFIPS = {};
chartData.forEach(function(d) { stateByFIPS[d.FIPS] = d.state; });
//Pair country with state id
var abbrevByFIPS = {};
chartData.forEach(function(d) { abbrevByFIPS[d.FIPS] = d.abbrev; });
//Append individual chart div
var chartContainer = d3.select(".gia-chart").append("div")
.attr("class", "g-chart-container")
//Attaches a data attribute to each chart
.attr('data-importer', country);
//Sets dimensions
var margin = {top: 0, left: 5, bottom: 5, right: 5},
width = d3.select(".g-chart-container").node().clientWidth,
width = width - margin.left - margin.right,
mapRatio = .65,
height = width * mapRatio;
//Tells the map what projection to use
var projection = d3.geo.albersUsa()
.scale(width *1.25)
.translate([width / 2, height / 2]);
//Tells the map how to draw the paths from the projection
var path = d3.geo.path()
.projection(projection);
//Appends div for country sentence
var chartSentence = chartContainer.append("div")
.attr("class", "g-sentence-div");
//Appends spans for country sentence
var chartStart = chartSentence.append("span")
.text("Where ")
.attr("class", "g-sentence");
var chartCountry = chartSentence.append("span")
.text(country)
.attr("class", "g-country");
var chartEnd = chartSentence.append("span")
.text(" is a top-10 importer")
.attr("class", "g-sentence");
//Appends the country map div to each container
var chart = chartContainer.append("div")
.attr("class", "g-chart");
//Appened svg to page
var map = chart.append("svg")
.style('height', height + 'px')
.style('width', width + 'px');
//Appends tooltip
var div = chartContainer.append("div")
.attr("class", "tooltip");
//Creates geo feature array for each map using the data for each country
var features = topojson.feature(us, us.objects.states).features.filter( function ( feature ) {
return feature.id in dataByFIPS;
});
//Append states
map.append("g")
.attr("class", "states")
.selectAll("path")
.data(features)
.enter().append("path")
.attr("d", path)
.style("fill", "#eaeaea")
//Also attaches hovers to states for greater area
.on("mouseover", mouseover)
.on("mouseout", mouseout);
//Append proportional bubbles
map.append("g")
.attr("class", "bubble-group")
.selectAll(".bubble")
.data(features)
.enter().append("circle")
//Class by state abbreviation
.attr("class", function(d) {
return "default " + abbrevByFIPS[d.id]
})
.attr("d", path)
.attr("transform", function(d) {
var centroid = path.centroid( d );
return "translate(" + centroid + ")";
})
.attr("r", function(d) {
var r = Math.sqrt(dataByFIPS[d.id]/200);
return r;
})
.on("mouseover", mouseover)
.on("mouseout", mouseout);
//Show tooltip on hover
function mouseover(d) {
highlightState(d.id);
}
//Hide tooltip on exit
function mouseout(d) {
dehighlightStates();
}
//RESPONSIVENESS
return {
resize: function() {
//Get page width
var w = d3.select(".g-chart-container").node().clientWidth;
//Adjust things when the window size changes
width = w - margin.left - margin.right;
height = width * mapRatio;
//Update projection
var newProjection = d3.geo.albersUsa()
.scale(width*1.25)
.translate([width / 2, height / 2]);
//Update path
path = d3.geo.path()
.projection(newProjection);
//Resize the map container
map
.style('width', width + 'px')
.style('height', height + 'px');
//Resize the map
map.selectAll("path").attr('d', path);
//Resize the bubbles
map.selectAll("circle")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")";})
}
}
//Highlights all corresponding bubbles and states across all maps
function highlightState (fips) {
//Get the FIPS from the state abbrevation array
var postal = abbrevByFIPS[fips];
allCountries.forEach(function (country) {
//Select all circles with a matching FIPS
var circle = d3.select('[data-importer="' + country + '"] .' + postal);
//Change their class to be highlighted on hover
circle.classed('highlighted', true);
//Select all tooltips with match FIPS
var tooltip = d3.select('[data-importer="' + country + '"] .tooltip');
//Get the associated data by country and FIPS
var data = dataByImporter[country][fips];
//Align the tooltip with the bounding rectangle around it
var bcr = circle.node().getBoundingClientRect();
var left = bcr.right;
var top = (bcr.top + bcr.bottom) / 2;
//Style and fill the tooltip
tooltip
.html("<span class='bolded'>" + postal + "</span><br>" + "$" + format(Math.round(data.value)))
.style("left", left + "px")
.style("top", top + "px")
.classed('visible', function(d){
//Only show tooltips when the value is not 0
return data.value != '0';
});
});
}
function dehighlightStates () {
//Unhighlight the circle by class
d3.selectAll('circle.highlighted').classed('highlighted', false);
//Hide the tooltip by class
allCountries.forEach(function (country) {
var tooltip = d3.select('[data-importer="' + country + '"] .tooltip');
tooltip
.classed('visible', false);
});
}
}
}
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/queue-async/1.0.7/queue.min.js
https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js