Eric G
Eric G

Reputation: 742

Why do my circle nodes not draw correctly in the d3 force directed graph?

I am not sure why my circle nodes are not drawing in this case. My goal is to have fine-tuned control over the element being created for each node based on the node data (d). I do not always want circles.

Looking at what this code generates, I see:

generated html

circle are being added to the svg with a radius and fill color. However, they are not drawn.

But, if I do:

    const node = svg
      .append('g')
      .attr('stroke', '#fff')
      .attr('stroke-width', 1.5)
      .selectAll()
      .data(nodes)
      .join((enter) => enter.append('circle'))
      .attr('r', 16)
      .attr('fill', (d) => color(d.group))

I see:

enter image description here

The svg code is identical. I do see that there is a circle[Attributes Style] applied to each circle element. But, I am not sure why that is there in the second case, but not the first.

How can I use a function to create different node elements based on the node data? How does my code need to change so the circles will be drawn while still using a function passed to enter.append so I can return an arbitrary element?

    const width = 1000
    const height = 400

    const node_data = Array.from({ length: 5 }, () => ({
      group: Math.floor(Math.random() * 3),
    }))

    const edge_data = Array.from({ length: 10 }, () => ({
      source: Math.floor(Math.random() * 5),
      target: Math.floor(Math.random() * 5),
      value: Math.floor(Math.random() * 10) + 1,
    }))

    const links = edge_data.map((d) => ({ ...d }))
    const nodes = node_data.map((d, index) => ({ id: index, ...d }))
    
    const color = d3.scaleOrdinal(d3.schemeCategory10)

    const svg = d3.select('#chart')

    const simulation = d3
      .forceSimulation(nodes)
      .force(
        'link',
        d3
          .forceLink(links)
          .id((d) => d.id)
          .distance((d) => 100)
      )
      .force('charge', d3.forceManyBody())
      .force('center', d3.forceCenter(width / 2, height / 2))
      .on('tick', ticked)

    const link = svg
      .append('g')
      .attr('stroke', '#999')
      .attr('stroke-opacity', 0.6)
      .selectAll()
      .data(links)
      .join('line')
      .attr('stroke-width', (d) => Math.sqrt(d.value))

    const node = svg
      .append('g')
      .attr('stroke', '#fff')
      .attr('stroke-width', 1.5)
      .selectAll()
      .data(nodes)
      .join((enter) =>
        enter.append((d) => {
          return document.createElement('circle')
        })
      )
      .attr('r', 16)
      .attr('fill', (d) => color(d.group))      

    node.append('title').text((d) => `hello ${d.id}`)

    function ticked() {
      link
        .attr('x1', (d) => d.source.x)
        .attr('y1', (d) => d.source.y)
        .attr('x2', (d) => d.target.x)
        .attr('y2', (d) => d.target.y)

      node.attr('cx', (d) => d.x).attr('cy', (d) => d.y)
    }
    .graph {
      width: 1000px;
      height: 400px;
    }
<script src="https://d3js.org/d3.v7.min.js" charset="utf-8"></script>
<svg ref="chart" id="chart" class="graph"></svg>

Upvotes: 0

Views: 92

Answers (1)

Akash Ram
Akash Ram

Reputation: 317

kindly update the code .join(enter => { const elementType = d => document.createElementNS('http://www.w3.org/2000/svg', 'circle'); return enter.append(elementType); })

    const width = 1000
    const height = 400

    const node_data = Array.from({ length: 5 }, () => ({
      group: Math.floor(Math.random() * 3),
    }))

    const edge_data = Array.from({ length: 10 }, () => ({
      source: Math.floor(Math.random() * 5),
      target: Math.floor(Math.random() * 5),
      value: Math.floor(Math.random() * 10) + 1,
    }))

    const links = edge_data.map((d) => ({ ...d }))
    const nodes = node_data.map((d, index) => ({ id: index, ...d }))
    
    const color = d3.scaleOrdinal(d3.schemeCategory10)

    const svg = d3.select('#chart')

    const simulation = d3
      .forceSimulation(nodes)
      .force(
        'link',
        d3
          .forceLink(links)
          .id((d) => d.id)
          .distance((d) => 100)
      )
      .force('charge', d3.forceManyBody())
      .force('center', d3.forceCenter(width / 2, height / 2))
      .on('tick', ticked)

    const link = svg
      .append('g')
      .attr('stroke', '#999')
      .attr('stroke-opacity', 0.6)
      .selectAll()
      .data(links)
      .join('line')
      .attr('stroke-width', (d) => Math.sqrt(d.value))

    const node = svg
      .append('g')
      .attr('stroke', '#fff')
      .attr('stroke-width', 1.5)
      .selectAll()
      .data(nodes)
       .join(enter => {
    const elementType = d => document.createElementNS('http://www.w3.org/2000/svg', 'circle');
    return enter.append(elementType);
  })
      .attr('r', 16)
      .attr('fill', (d) => color(d.group))      

    node.append('title').text((d) => `hello ${d.id}`)

    function ticked() {
      link
        .attr('x1', (d) => d.source.x)
        .attr('y1', (d) => d.source.y)
        .attr('x2', (d) => d.target.x)
        .attr('y2', (d) => d.target.y)

      node.attr('cx', (d) => d.x).attr('cy', (d) => d.y)
    }
.graph {
      width: 1000px;
      height: 400px;
    }
<script src="https://d3js.org/d3.v7.min.js" charset="utf-8"></script>
<svg ref="chart" id="chart" class="graph"></svg>

Upvotes: 0

Related Questions