Built with blockbuilder.org
forked from dhexonian's block: bubble chart for two periods
xxxxxxxxxx
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js"></script>
<script src="https://d3plus.org/js/d3plus-text.v0.9.full.min.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html {
background-color: hsl(0, 0%, 100%);
overflow-y: scroll;
font-family: MetricWeb, sans-serif;
}
.annotation-note-title {
/* font-weight: bold; */
fill: black;
}
.annotation-note-label {
/* font-weight: bold; */
fill: grey;
}
.bounding {
margin-left: 30px;
margin-right: 30px;
margin-top: 30px;
}
#chart {
width: 100%;
max-width: 850px;
}
.header {
width: 50px;
border-top: 2.5px solid #000000;
margin-bottom: 0px;
display: block;
}
.main {
fill: #33302e;
font-size: 17px;
}
.sub {
fill: #66605c;
display: inline-block;
font-size: 13px;
opacity: 0.90;
}
.y-axis path,
.x-axis path {
stroke: #757575;
shape-rendering: crispEdges;
stroke-width: 1px;
}
.x-axis text,
.y-axis text {
font-size: 11px;
fill: #66605c;
opacity: 0.95;
}
.x-axis .tick line {
stroke: #66605c;
stroke-width: 0.8px;
opacity: 0.2;
}
.y-axis .tick line {
stroke: #66605c;
stroke-width: 0.8px;
opacity: 0.2;
}
.footnote text {
font-size: 9px;
opacity: 0.85;
fill: #66605c;
}
</style>
<body>
<div class="bounding">
<div class="header"></div>
<div id="chart"></div>
</div>
</body>
<script>
var dataset;
d3.csv("data.csv", function (err, data) {
dataset = data;
draw(dataset);
});
function draw(data) {
"use strict";
var margin = {
top: 150,
right: 50,
bottom: 180,
left: 20
},
container_width = 850,
container_height = 570,
width = container_width - margin.left - margin.right,
height = container_height - margin.top - margin.bottom;
var svg = d3
.select("#chart")
.append("svg")
.attr("id", "svg_container")
.attr("viewBox", `0 0 ${container_width} ${container_height}`)
.attr("preserveAspectRatio", "xMidYMid")
.append("g")
.attr("id", "container")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var main_padding = 20;
var sub_padding = 45;
var chart = d3.select('#svg_container')
.append('g')
.attr('id', 'text-label')
chart.append("text")
.attr("class", "main")
.text("Economic growth prospects of emerging economies")
.attr(
"transform",
"translate(" + 0 + "," + main_padding + ")"
);
chart
.append("text")
.attr("class", "sub")
.text("Average percentage point change in real GDP growth")
.attr(
"transform",
"translate(" + 0 + "," + sub_padding + ")"
);
var footer_data = ["Source: IMF World Economic Outlook"]
svg
.append("g")
.attr('class', 'footnote')
.selectAll('text')
.data(footer_data)
.enter()
.append('text')
.text(function (d) { return d; })
.attr("transform", function (d, i) {
return "translate(" + (-margin.left) + "," + (height + margin.bottom / 1.3 + i * 14) + ")"
});
var y_extent = d3.extent(data, function (d) {
return d.Population;
})
var y_scale = d3
.scaleLinear()
.domain([-2, 9])
.range([height, 0]);
var r_scale = d3
.scaleSqrt()
.domain([0, y_extent[1]])
.range([2, width / 20]);
var base = 0
var newbase = 5
var gap = 20
// base = current circle's left diameter point
// base + d = current circle's center
// newbase = next circle's left diameter point
data.forEach(function (d, i) {
base = newbase;
let radius = 1 * r_scale(d.Population);
newbase = newbase + 2 * radius + gap;
d['r'] = radius;
d['cx'] = base + radius;
});
var maxWidth = d3.max(data, function (d) {
return d.cx + d.r;
})
// console.log(data);
// console.log(maxWidth,'yer');
// console.log(width);
var yAxis = svg.append("g").attr("class", "y-axis");
yAxis
.attr("transform", `translate(${0},0 )`)
.call(
d3
.axisRight(y_scale)
.tickSizeInner(width)
.tickSizeOuter(0)
.tickPadding(35)
.tickFormat(function (d) {
return d + '%'
})
)
.call(g => g.select(".domain").remove())
.attr('text-anchor', 'end');
d3.select(".y-axis")
.selectAll(".tick")
.each(function (d, i) {
if (i % 2 != 0) {
d3.select(this).remove();
}
});
var groups = svg.append("g")
.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("transform", function (d) {
return `translate(${d.cx},0)`;
})
var arrowColor = "#a9355a";
svg
.append("svg:defs").append("svg:marker")
.attr('class', 'skirty')
.attr("id", "arrow")
.attr("refX", 4)
.attr("refY", 4)
.attr("markerWidth", 30)
.attr("markerHeight", 30)
.attr("markerUnits", "userSpaceOnUse")
.attr("orient", "auto")
.append("path")
.attr("d", "M 0,0 8,4 0,8 3,4")
.style("fill", arrowColor);
var keys = data.columns.slice(1, 3);
groups.append('text')
.attr('class', 'yer')
.text(function (d) {
return d.Geography
})
.attr('text-anchor', 'end')
.attr('dominant-baseline', 'hanging')
.attr('x', 0)
.attr('y', height)
.attr('font-size', '14')
.attr('fill', '#66605c')
.attr('transform', function (d) {
return `rotate(${-90} ${0} ${height}) translate(${-5} ${0})`
})
.attr('dy', '-0.35rem')
dataset.forEach(function (d) {
d.text = d.Geography;
})
groups
.append('line')
.attr("x1", 0)
.attr("y1", function (d) {
let key = keys[0];
return y_scale(d[key]);
})
.attr("x2", 0)
.attr("y2", function (d) {
let key = keys[1];
return y_scale(d[key]);
})
.attr("stroke-width", 2.0)
.attr('stroke', arrowColor)
.attr('marker-end', "url(#arrow)");
var primaryColor = '#b18dc2'
groups.selectAll(".circle")
// [{},{}]
.data(function (d) {
return keys.map(function (key) {
return { key: key, val: y_scale(d[key]), size: r_scale(d.Population) };
});
})
.enter()
.append("circle")
.attr("cy", function (d, i) {
return d.val;
})
.attr('r', function (d) {
return d.size;
})
.attr("fill", function (d, i) {
return i == 0 ? primaryColor : "none"
})
.attr('stroke', function (d, i) {
return i == 0 ? 'none' : primaryColor
})
.attr('stroke-width', function (d, i) {
return i == 0 ? 'none' : "3"
})
.attr("stroke-dasharray", function (d, i) {
return i == 0 ? '0' : "4"
})
.attr('opacity', '0.5')
var z_scale = d3
.scaleOrdinal()
.domain(['2013-2017', '2018-2022'])
.range([
primaryColor,
primaryColor
]);
var compLegend = d3.legendColor().orient('vertical')
.shape("circle")
.shapeRadius(10)
.labelOffset(7)
.shapePadding(20)
.scale(z_scale);
svg.append("g")
.attr("class", "compLegend")
.attr("transform", `translate(${0},${-margin.top / 2})`)
.call(compLegend);
var cells = d3.selectAll(".cell");
cells
.select("text")
.style("text-anchor", "start")
.style("font-size", 12)
.attr("dominant-baseline", "central");
var offset = 0;
cells.each(function (d, i) {
let d3sel = d3.select(this);
if (i == 1) {
let shape = d3sel.select('circle');
shape.style('fill', 'none');
shape.style('stroke-width', '2');
shape.style('stroke-dasharray', '3');
shape.style('stroke', primaryColor);
}
let textWidth = d3sel
.select("text")
.node()
.getComputedTextLength();
let offsetInc = textWidth + compLegend.shapeRadius() * 2 + compLegend.shapePadding() + compLegend.labelOffset();
d3sel.attr("transform", "translate(" + offset + " 0)");
let xTranslate = compLegend.shapeRadius() + compLegend.labelOffset();
// this 0 for circle only
d3sel.select("text").attr("transform", `translate(${xTranslate},${0})`)
offset += offsetInc;
});
}
</script>
</html>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.25.6/d3-legend.min.js
https://d3plus.org/js/d3plus-text.v0.9.full.min.js