Skip to content

Instantly share code, notes, and snippets.

@emeeks
Created March 3, 2017 03:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emeeks/6a77dbcf149b4b9e772b30af71d11b06 to your computer and use it in GitHub Desktop.
Save emeeks/6a77dbcf149b4b9e772b30af71d11b06 to your computer and use it in GitHub Desktop.
Annotated Venn

Venn Diagram as Annotation

This demonstrates how to use d3-annotation() with d3.forceSimulation to draw a Venn Diagram. In this the Venn shows a population of data visualizers (a semi-mythical and apparently controversial profession) that describe their organization as having data visualization professionals either embedded in other teams (green) or in a dedicated data visualization team (tan) or having both (green and tan). This uses d3.packEnclose to determine the size and location of the annotation subject, which utilizes d3.annotationCalloutCircle.

d3-annotation by Susie Lu.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link href='https://fonts.googleapis.com/css?family=Lato:300,900' rel='stylesheet' type='text/css'>
<style>
:root {
--annotation-color: #E8336D;
}
body{
background-color: whitesmoke;
}
svg {
background-color: white;
font-family: 'Lato';
}
line {
stroke:#eae4e4;
}
.editable .annotation-subject, .editable .annotation-textbox {
cursor: move;
}
.annotation path {
stroke: var(--annotation-color);
fill: rgba(0,0,0,0);
}
.annotation path.connector-arrow{
fill: var(--annotation-color);
}
.annotation text {
fill: var(--annotation-color);
}
.annotation-title {
font-weight: bold;
}
circle.handle {
stroke-dasharray: 5;
stroke: var(--annotation-color);
fill: rgba(255, 255, 255, .5);
cursor: move;
stroke-opacity: .4;
}
circle.handle.highlight {
stroke-opacity: 1;
}
.annotation-note-bg {
fill: rgba(255, 255, 255, 0);
}
.dedicated-line {
stroke: #00897b;
stroke-width: 5px;
}
.embedded-line {
stroke: #e4b0b0;
stroke-width: 5px;
}
.embedded path {
stroke: #006d62;
fill: #006d62;
fill-opacity: 0.25;
stroke-width: 2px;
}
.dedicated path {
stroke: #b68c8c;
fill: #b68c8c;
fill-opacity: 0.25;
stroke-width: 2px;
}
</style>
</head>
<body>
<svg width=960 height=500>
<defs> <pattern id="half-fill" width="10" height="10">
<line x1="2.5" x2="2.5" y1="0" y2="10" class="dedicated-line"></line>
<line x1="7.5" x2="7.5" y1="0" y2="10" class="embedded-line"></line>
</pattern> </defs>
</svg>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdn.rawgit.com/susielu/d3-annotation/master/d3-annotation.js"></script>
<script>
var svg = d3.select("svg"),
width = 1000,
height = 500;
var color = {
// "a": "#e4b0b0",
"a": "#00897b",
"b": "#e4b0b0",
"c": "url(#half-fill)"
}
let groups = ["embedded", "dedicated"]
const nodes = d3.range(600).map((d,i) => {
if (i < 25) {
return { color: "c", embedded: true, dedicated: true, center: 250 }
}
if (i < 400) {
return { color: "b", embedded: false, dedicated: true, center: 415 }
}
return { color: "a", embedded: true, dedicated: false, center: 100 }
})
var simulation = d3.forceSimulation()
.force("collision", d3.forceCollide(6).iterations(1))
.force("x", d3.forceX(a => a.center).strength(0.05))
.force("y", d3.forceY(0).strength(0.05))
.force("center", d3.forceCenter(width / 2, height / 2))
.alphaDecay(0.001)
.alpha(0.25)
.nodes(nodes)
.on("tick", ticked)
.stop()
simulation.tick()
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", 5)
.style("stroke", "black")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.style("fill", d => color[d.color])
setTimeout(() => { simulation.restart() }, 2500);
let points = groups.map(p => nodes
.filter(d => d[p])
.map(d => ({ x: d.x, y: d.y, r: 5 })))
let circle = points.map( p => d3.packEnclose(p))
const annotations = [{
note: { label: "embedded in other teams",
},
data: {id : "Data Visualization Professionals"},
dy: 93,
dx: -230,
x: circle[0].x,
y: circle[0].y,
type: d3.annotationCalloutCircle,
className: "embedded",
subject: {
radius: circle[0].r,
radiusPadding: 5
}
},
{
note: { label: "in teams dedicated to data visualization",
},
data: {id : "Data Visualization Professionals"},
dy: 93,
dx: 230,
x: circle[1].x,
y: circle[1].y,
className: "dedicated",
type: d3.annotationCalloutCircle,
subject: {
radius: circle[1].r,
radiusPadding: 5
}
}
]
const makeAnnotations = d3.annotation()
.annotations(annotations)
.accessors({ x: d => d.x , y: d => d.y, title: d => d.id })
svg.insert("g", "g")
.attr("class", "annotation-test")
.call(makeAnnotations)
function ticked() {
node
.attr("cx", d => d.x)
.attr("cy", d => d.y);
makeAnnotations.annotations()
.forEach((d, i) => {
let points = nodes
.filter(d => d[groups[i]])
.map(d => ({ x: d.x, y: d.y, r: 5}))
console.log("points", points)
circle = d3.packEnclose(points)
d.position = { x: circle.x, y: circle.y }
d.subject.radius = circle.r + 2
})
makeAnnotations.update()
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment