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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more