block by emeeks 6a77dbcf149b4b9e772b30af71d11b06

Annotated Venn

Full Screen

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.

index.html

<!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>