Public
Edited
Mar 5, 2024
1 fork
Insert cell
Insert cell
semiCircles( _.sortBy(testData, d => d.value).reverse(), { // sort data so largest circles at the back
width: width,
margin: {top: 10, right: 10, bottom: 30, left: 120},
})
Insert cell
function semiCircles (data, {
f = d3.format(','),
margin = {top: 10, right: 50, bottom: 30, left: 50},
labelKey = 'label', // key for group in data
valueKey = 'value', // key for group in data
width = 900,
height = 350,
bgdColour= '#ffffff', // svg colour and bgd rectangle that cuts half the circles
stroke = '#a36aa5', // circles stroke
fill = '#66629c', // circle colour
textFill = ['#66629c', '#a36aa5'], // labels row 1 and 2
opacity = 0.9, // fade of circles
circleSizeMax = height/2 // limit the size of the largest circle
} = {}) {

// set the dimensions and margins of the graph
const w = width - margin.left - margin.right
const h = height - margin.top - margin.bottom

const max = d3.max(data, d => d[valueKey]);
const center = height / 4; // this is the centre of the circles (doesn't have to be center of svg)

const svg = DOM.svg(width, height);
const sel = d3.select(svg)
const g = sel.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);

const sqrtScale = d3.scaleSqrt()
.domain([0, max])
.range([0, circleSizeMax])
const linearScale = d3.scaleLinear()
.domain([0, data.length])
.range([(circleSizeMax/2), w]);

const groups = g.selectAll('g.g-sc')
.data(data)
.join('g')
.attr('class', 'g-sc')
.attr('transform', (d, i) => `translate(${linearScale(i)},${center})`);

groups.append('circle')
.attr('class', 'c-100')
.attr('r', d => sqrtScale(d[valueKey]))
.attr('fill', fill)
.attr('stroke', bgdColour)
.style('opacity', opacity)
.attr('stroke-width', 2);

// makes circles into a semi-circles by overlapping a rectangle
g.append('rect')
.attr('x', margin.left * -1)
.attr('y', margin.top * -1)
.attr('height', center + margin.top)
.attr('width', width)
.attr('fill', bgdColour)

g.append('line')
.attr('x1', margin.left * -1 + 5)
.attr('x2', w - margin.right)
.attr('y1', center)
.attr('y2', center)
.attr('stroke', stroke)
.attr('stroke-linecap', 'round')
.attr('stroke-width', 4);

const groupsText = g.selectAll('g.g-text')
.data(data)
.join('g')
.attr('class', 'g-text')
.attr('transform', (d, i) => `translate(${linearScale(i)},${center})`);

groupsText.append('text')
.attr('x', 0)
.attr('dy', -30)
.style('fill', textFill[0])
.style('text-anchor', 'middle')
.text(d => d[labelKey]).raise()

groupsText.append('text')
.attr('x', 0)
.attr('dy', -10)
.style('fill', textFill[1])
.style('text-anchor', 'middle')
.style('font-weight', '600')
.text(d => f(d[valueKey]))

return svg;

}
Insert cell
testData = [ // planet sizes/diameter in km (NASA)
{
label: 'Mercury',
value: 4879
},
{
label: 'Venus',
value: 12104
},
{
label: 'Earth',
value: 12756
},
{
label: 'Mars',
value: 6792
},
{
label: 'Jupiter',
value: 142984
}
]
Insert cell
<!-- not needed for visual -->
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<style>
text {
font-family:'Space Mono',monospace;
fill: #130C0E;
font-size: 13px;
}
</style>
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more